package org.jeecg.modules.business.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.jeecg.common.api.dto.message.MessageDTO;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.exception.JeecgBootException;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.common.util.DateUtils;
import org.jeecg.common.util.TokenUtils;
import org.jeecg.modules.business.dto.*;
import org.jeecg.modules.business.entity.*;
import org.jeecg.modules.business.enums.*;
import org.jeecg.modules.business.mapper.BusRoomBookingOrdersMapper;
import org.jeecg.modules.business.service.*;
import org.jeecg.modules.business.strategy.PricingStrategyFactory;
import org.jeecg.modules.business.util.MxTools;
import org.jeecg.modules.business.vo.*;
import org.jeecg.modules.fw.entity.FwRoomClean;
import org.jeecg.modules.fw.entity.FwRoomExamine;
import org.jeecg.modules.fw.entity.FwRoomLock;
import org.jeecg.modules.fw.entity.FwRoomRepair;
import org.jeecg.modules.fw.service.IFwRoomCleanService;
import org.jeecg.modules.fw.service.IFwRoomExamineService;
import org.jeecg.modules.fw.service.IFwRoomLockService;
import org.jeecg.modules.fw.service.IFwRoomRepairService;
import org.jeecg.modules.fw.vo.FwRoomLockVo;
import org.jeecg.modules.fw.vo.FwRoomRepairVo;
import org.jeecg.modules.kc.entity.KcDepositoryInGoods;
import org.jeecg.modules.kc.service.IKcDepositoryInGoodsService;
import org.jeecg.modules.rooms.Enum.ChargeTypeEnum;
import org.jeecg.modules.rooms.Enum.RoomStatusEnum;
import org.jeecg.modules.rooms.Enum.TimeOutRuleEnum;
import org.jeecg.modules.rooms.Vo.BookingRealtimeVo;
import org.jeecg.modules.rooms.Vo.FloorBuildingRoomVo;
import org.jeecg.modules.rooms.Vo.LivingContinueVo;
import org.jeecg.modules.rooms.Vo.LivingRealtimeVo;
import org.jeecg.modules.rooms.entity.*;
import org.jeecg.modules.rooms.service.*;
import org.jeecg.modules.system.entity.SysUser;
import org.jeecg.modules.system.service.ISysUserService;
import org.jeecg.modules.system.service.impl.SysBaseApiImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @Description: 酒店预定订单
 * @Author: jeecg-boot
 * @Date:   2023-03-25
 * @Version: V1.0
 */
@Slf4j
@Service
public class BusRoomBookingOrdersServiceImpl extends ServiceImpl<BusRoomBookingOrdersMapper, BusRoomBookingOrders> implements IBusRoomBookingOrdersService {

    @Autowired
    private BusBookingRoomsServiceImpl bookingRoomsService;

    @Autowired
    private BusBookingBatchServiceImpl bookingBatchService;

    @Resource
    private IBusCustomerService customerService;

    @Resource
    private IBusRoomsLivingOrderService roomsLivingOrderService;

    @Resource
    private IBusLivingCustomerService livingCustomerService;

    @Resource
    private IBusBookingLayoutDayPriceService dayPriceService;

    @Resource
    private IBusLivingLayoutDayPriceService livingLayoutDayPriceService;

    @Resource
    IBusSchemeLayoutDailyPriceService schemeLayoutDailyPriceService;

    @Resource
    private IBusTeamService teamService;

    @Resource
    private CesRoomsServiceImpl roomsService;

    @Resource
    private IBusOrderFeeService feeService;

    @Resource
    private IBusMarketCouponsCashUsedService marketCouponsCashUsedService;

    @Resource
    private IBusMemberCardService memberCardService;

    @Resource
    private IBusMarketMemberService marketMemberService;

    @Resource
    private IBusMemberBalanceLogService memberBalanceLogService;

    @Resource
    private  IBusRoomPayTypeService payTypeService;

    @Resource
    private CesRoomLayoutServiceImpl layoutService;

    @Resource
    private CesRoomBuildingFloorServiceImpl buildingFloorService;

    @Resource
    private CesAllDayPriceRuleServiceImpl allDayPriceRuleService;
    @Resource
    private CesGoodsServiceImpl cesGoodsServiceImpl;

    @Resource
    private IBusMarketAgreementUnitService marketAgreementUnitService;

    @Resource
    IBusMarketAgreementCustomerService marketAgreementCustomerService;

    @Resource
    private IFwRoomCleanService fwRoomCleanService;

    @Resource
    private IFwRoomLockService fwRoomLockService;

    @Resource
    private IFwRoomRepairService fwRoomRepairService;

    @Resource
    private IFwRoomExamineService fwRoomExamineService;

    @Resource
    private CesHourRoomRuleServiceImpl hourRoomRuleService;

    @Resource
    private IBusOrderFeeGoodsService orderFeeGoodsService;

    @Resource
    private IBusCancelService cancelService;
    @Autowired
    private IBusParamPresetService busParamPresetService;

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Resource
    private IKcDepositoryInGoodsService depositoryInGoodsService;

    @Resource
    private ISysUserService sysUserService;

    @Resource
    private IBusHotelService busHotelService;

    @Resource
    ICesHouseLongRentChargeService houseLongRentChargeService;
    @Resource
    ICesHouseLongRentMeterLogService houseLongRentMeterLogService;

    @Resource
    private SysBaseApiImpl sysBaseAPI;

    @Resource
    private PricingStrategyFactory pricingStrategyFactory;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String bookingOrderSave(BookingOrderSaveDto item, Boolean isTeam,Boolean isLiving,String hotelId) {
        isTeam = isTeam != null && isTeam;
        LoginUser user = TokenUtils.getAuthUser();
        item.getOrderInfo().setCreateTime(new Date());
        if(user!=null)
        {
            item.getOrderInfo().setCreateUser(user.getId());
        }
        if (StrUtil.isEmpty(item.getOrderInfo().getBookingOrdersNo())){
            SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS");
            item.getOrderInfo().setBookingOrdersNo("YD"+sdf.format(System.currentTimeMillis()));
        }
        // 检测是否时间安排上有冲突
        List<String> roomIds = item.getRoomIds().stream().map(BookingLayoutRoomsDto::getRoomId).collect(Collectors.toList());

        if(item.getConfirm() == null || !item.getConfirm()){
            if(!roomIds.isEmpty()) {
                List<BusBookingRooms> bookingRooms = baseMapper.conflictBookingTime(item.getOrderInfo().getArrivalTime(),item.getOrderInfo().getDueOutTime(),roomIds);
                if(!bookingRooms.isEmpty()) {
                    String roomNames = bookingRooms.stream().map(p->p.getRoomId()).collect(Collectors.joining("|"));
                    throw new JeecgBootException("房间["+roomNames+"]与预约时间有冲突");
                }
            }
        }

        if(item.getOrderInfo().getCustomerSource() == null || item.getOrderInfo().getCustomerSource().isEmpty())
            throw new JeecgBootException("客人来源不能为空");
        if(item.getOrderInfo().getCustomerType() == null)
            throw new JeecgBootException("客人类型不能为空");
        if(CustomerTypeEnum.val(item.getOrderInfo().getCustomerType()) == null)
            throw new JeecgBootException("客人类型不正确");
        if(item.getOrderInfo().getCustomerType().equals(CustomerTypeEnum.VIP.getKey())
                && (item.getOrderInfo().getVipCustomerId() == null || item.getOrderInfo().getVipCustomerId().isEmpty()))
            throw new JeecgBootException("客人类型为会员时，请选择会员");
        if(item.getOrderInfo().getCustomerType().equals(CustomerTypeEnum.PROTOCOL.getKey())
                && (item.getOrderInfo().getContractTeamId() == null || item.getOrderInfo().getContractTeamId().isEmpty()
                    || item.getOrderInfo().getContractTeamProtocolId() == null || item.getOrderInfo().getContractTeamProtocolId().isEmpty()
                    )
        )
            throw new JeecgBootException("客人类型为协议单位时，请选择协议单位和相关协议");
        if(item.getOrderInfo().getCustomerType().equals(CustomerTypeEnum.PROXY.getKey())
                && (item.getOrderInfo().getAgencyName() == null || item.getOrderInfo().getAgencyName().isEmpty())
        )
            throw new JeecgBootException("客人类型为中介时，请填写中介信息");
        // 钟点房判断，计费项目判定
        if(item.getOrderInfo().getBookingType().equals(CheckInTypeEnum.HOUR_TIME.getKey())) {
            if(item.getOrderInfo().getHourRoomId() == null || item.getOrderInfo().getHourRoomId().isEmpty())
                throw new JeecgBootException("钟点房计费项目不能为空");
        }

        if(item.getOrderInfo().getBookingOrdersType() == null)
            throw new JeecgBootException("参数错误");
        // 【添加客人信息 Start】
            if(item.getContactName() != null && !item.getContactName().isEmpty() && item.getPhone() != null && !item.getPhone().isEmpty()){
                BusCustomer customer = customerService.getOne(Wrappers.<BusCustomer>query().eq("name",item.getContactName())
                                .eq("hotel_id",hotelId)
                        .eq("phone",item.getPhone()));
                if(customer == null) {
                    // save customer
                    BusCustomer newCustomer = new BusCustomer();
                    newCustomer.setCreateTime(new Date());
                    if(user!=null) {
                        newCustomer.setCreateUser(user.getId());
                        newCustomer.setTenantId(user.getRelTenantIds());
                    }
                    newCustomer.setHotelId(hotelId);
                    newCustomer.setName(item.getContactName());
                    newCustomer.setPhone(item.getPhone());
                    newCustomer.setCertType(1); // 身份证
                    newCustomer.setCertNo(item.getCertNo()); // 身份证
                    customerService.save(newCustomer);
                    item.getOrderInfo().setContactId(newCustomer.getId());
                } else {
                    item.getOrderInfo().setContactId(customer.getId());
                }
            } else if(item.getLivingRoomDayPrices() != null && !item.getLivingRoomDayPrices().isEmpty()){

            } else if (item.getOrderInfo().getBookingType() == 5) {

            }else throw new JeecgBootException("客人信息不能为空");

        // 【添加客人信息 end】
        // 散客预定
        if(item.getOrderInfo().getBookingOrdersType().equals(BookingOrdersType.NORMAL.getKey())) {
            if(item.getOrderInfo().getArrivalTime() == null || item.getOrderInfo().getDueOutTime() == null)
                throw new JeecgBootException("预抵时间和预离时间不能为空");
            if(item.getOrderInfo().getArrivalTime().getTime() < new Date().getTime() && !isLiving)
                throw new JeecgBootException("预抵时间不能小于当前时间");
            if(item.getOrderInfo().getArrivalTime().getTime() >= item.getOrderInfo().getDueOutTime().getTime())
                throw new JeecgBootException("预抵时间不能大于或者等于预离时间");
            // 非钟点房判定
            if(!item.getOrderInfo().getBookingType().equals(CheckInTypeEnum.HOUR_TIME.getKey())) {
                if(item.getOrderInfo().getDayCount() == null || item.getOrderInfo().getDayCount() <= 0)
                    throw new JeecgBootException("入住天数不能小于0");

            }
            if(item.getRoomIds() == null || item.getRoomIds().isEmpty())
                throw new JeecgBootException("请选择预定房间");
            if(hotelId == null || hotelId.isEmpty()) throw  new JeecgBootException("参数错误，酒店id");
            item.getOrderInfo().setHotelId(hotelId);
            // 保存order
            saveOrUpdate(item.getOrderInfo());
            String bookingId = item.getOrderInfo().getId();
            // todo 判定是否有安排冲突的房间
            List<BusBookingRooms> bookingRooms = new ArrayList<>();
            long mainCount = item.getRoomIds().stream().filter(t->t.getIsMain()!=null&&t.getIsMain()).count();

            item.getRoomIds().forEach(c->{
//                CesRooms room = roomsService.getById(c.getRoomId());
//                if(room == null) throw new JeecgBootException("房间不存在");
//                room.setBookingOrderId(bookingId);
//                roomsService.updateById(room);

                BusBookingRooms bookingRoomsItem = new BusBookingRooms();
                bookingRoomsItem.setBookingOrdersId(bookingId);
                if(c.getRoomId() != null && !c.getRoomId().isEmpty()){
                    bookingRoomsItem.setRoomStatus(2);
                } else {
                    bookingRoomsItem.setRoomStatus(1);
                }
                if(mainCount != 1) {
                    bookingRoomsItem.setIsMain(false);
                } else {
                    bookingRoomsItem.setIsMain(c.getIsMain());
                }
                bookingRoomsItem.setRoomId(c.getRoomId());
                bookingRoomsItem.setRoomLayoutId(c.getLayoutId());
                bookingRoomsItem.setBookingType(1);
                bookingRooms.add(bookingRoomsItem);
            });
            if(mainCount != 1) {
                bookingRooms.get(0).setIsMain(true);
            }
            bookingRoomsService.saveBatch(bookingRooms);

            if (item.getOrderInfo().getBookingType() != 5){
                // 处理预定每天的房型价格 Start
                if(item.getLivingRoomDayPrices() != null && !item.getLivingRoomDayPrices().isEmpty()) {
                    if(item.getLivingRoomDayPrices().stream().anyMatch(o->o.getPrice()==null || o.getDayTime() == null || o.getRoomId() == null))
                        throw new JeecgBootException("参数错误，请传入价格、日期、房型");
                    // 入住之后在save
                } else {
                    if(item.getLayoutDayPrices() == null || item.getLayoutDayPrices().isEmpty())
                        throw new JeecgBootException("未排房");
                    if(item.getLayoutDayPrices().stream().anyMatch(o->o.getPrice()==null || o.getDayTime() == null || o.getRoomLayoutId() == null))
                        throw new JeecgBootException("参数错误，请传入价格、日期、房型");
                    item.getLayoutDayPrices().forEach(g->{
                        long roomCount = item.getRoomIds().stream().filter(e->e.getLayoutId().equals(g.getRoomLayoutId())).count();
                        if(roomCount > 0) {
                            g.setBookingOrderId(item.getOrderInfo().getId());
                            g.setBookingType(BookingOrdersType.NORMAL.getKey());
                        }
                    });
                    List<String> lList = item.getRoomIds().stream().map(BookingLayoutRoomsDto::getLayoutId).collect(Collectors.toList());
                    dayPriceService.saveBatch(item.getLayoutDayPrices().stream().filter(l->lList.contains(l.getRoomLayoutId())).collect(Collectors.toList()));
                }
            }


            // 处理预定每天的房型价格 End

            // 预约时不添加联房和团队标识，入住时才添加
            if (isLiving) {
                if(isTeam) {
                    BusTeam team = new BusTeam();
                    List<BusTeam> existTeams = teamService.list(Wrappers.<BusTeam>query().eq("is_delete", 0).eq("hotel_id", hotelId)
                            .eq("rel_type",1)); // 1团队房，2联房
                    Integer sortIndex = 1;
                    List<Integer> indexs = existTeams.stream().map(BusTeam::getSortIndex).collect(Collectors.toList());
                    while(true){
                        Integer finalSortIndex = sortIndex;
                        if(indexs.stream().filter(s->s.equals(finalSortIndex)).count() == 0) {
                            break;
                        }
                        sortIndex++;
                    }
                    team.setBookingOrderId(bookingId);
                    team.setIsDelete(0);
                    team.setHotelId(hotelId);
                    team.setSortIndex(sortIndex);
                    team.setRelType(1);

                    teamService.save(team);
                } else {
                    // 散客同时居住
                    if(item.getRoomIds().size() > 1) {
                        addBusTeams(hotelId, bookingId);
                    }
                }
                redisTemplate.opsForZSet().remove("sys:cache:order:overTimerBooking", bookingId);
            } else {
                BusParamPreset paramPreset = busParamPresetService.getOne(Wrappers.<BusParamPreset>lambdaQuery().eq(BusParamPreset::getHotelId, hotelId));
                Calendar calendar = Calendar.getInstance();
                calendar.setTime(item.getOrderInfo().getArrivalTime());
                if (ObjectUtil.equals(paramPreset.getStatus(), 1)) {
                    calendar.add(Calendar.MINUTE, paramPreset.getMinute());
                }
                redisTemplate.opsForZSet().add("sys:cache:order:overTimerBooking", bookingId, calendar.getTimeInMillis());
            }
            //</editor-fold>
            roomsService.getReserveRoom();
            return item.getOrderInfo().getBookingOrdersNo();
            // 团队预定
        } else if(item.getOrderInfo().getBookingOrdersType().equals(BookingOrdersType.TEAM.getKey())) {
            if(item.getOrderInfo().getTeamName() == null || item.getOrderInfo().getTeamName().isEmpty())
                throw new JeecgBootException("请填写团队名称");
            if(item.getBatchRooms() == null || item.getBatchRooms().isEmpty())
                throw  new JeecgBootException("请添加批次信息");
            saveOrUpdate(item.getOrderInfo());
            item.getBatchRooms().forEach(c->{
                if(c.getArrivalTime() == null || c.getDueOutTime() == null)
                    throw new JeecgBootException("预抵时间和预离时间不能为空");
                if(c.getArrivalTime().getTime() < new Date().getTime())
                    throw new JeecgBootException("预抵时间不能小于当前时间");
                if(c.getArrivalTime().getTime() >= c.getDueOutTime().getTime())
                    throw new JeecgBootException("预抵时间不能大于或者等于预离时间");
                // 非钟点房判定
                if(!item.getOrderInfo().getBookingType().equals(CheckInTypeEnum.HOUR_TIME.getKey())) {
                    if(c.getDayCount() == null || c.getDayCount() <= 0)
                        throw new JeecgBootException("入住天数不能小于0");
                }
                if(c.getRoomIds() == null || c.getRoomIds().isEmpty())
                    throw new JeecgBootException("请选择批次房间");
                SimpleDateFormat sdfPc = new SimpleDateFormat("yyyyMMddHHmmssSSS");
                c.setBatchNo("YDPC"+sdfPc.format(System.currentTimeMillis()));
                c.setBookingOrdersId(item.getOrderInfo().getId());
                setBatchOrderInfo(item.getOrderInfo(),c);
                BusBookingBatch batchItem = copyBatchToBase(c);
                bookingBatchService.save(batchItem);
                List<BusBookingRooms> batchBookingRooms = new ArrayList<>();
                long mainCount = c.getRoomIds().stream().filter(t->t.getIsMain()!=null&&t.getIsMain()).count();
                c.getRoomIds().forEach(s-> {
                    BusBookingRooms bookingRoomItem = new BusBookingRooms();
                    bookingRoomItem.setRoomId(s.getRoomId());
                    bookingRoomItem.setRoomLayoutId(s.getLayoutId());
                    if(s.getRoomId() != null && !s.getRoomId().isEmpty()){
                        bookingRoomItem.setRoomStatus(2);
                    } else {
                        bookingRoomItem.setRoomStatus(1);
                    }
                    if(mainCount != 1) {
                        bookingRoomItem.setIsMain(false);
                    } else {
                        bookingRoomItem.setIsMain(s.getIsMain());
                    }
                    bookingRoomItem.setBookingType(2);
                    bookingRoomItem.setBookingBatchId(batchItem.getId());
                    bookingRoomItem.setBookingOrdersId(item.getOrderInfo().getId());
                    batchBookingRooms.add(bookingRoomItem);
                });
                if(mainCount != 1) {
                    batchBookingRooms.get(0).setIsMain(true);
                }
                bookingRoomsService.saveBatch(batchBookingRooms);
                // 以上处理批次关联的房间

                //批次中处理每天房型价格 Start
                if(c.getLayoutDayPrices() == null || c.getLayoutDayPrices().size() == 0)
                    throw new JeecgBootException("参数错误，批次中对应房型当天价格必传");
                if(c.getLayoutDayPrices().stream().anyMatch(o->o.getPrice()==null || o.getDayTime() == null || o.getRoomLayoutId() == null))
                    throw new JeecgBootException("参数错误，批次中请传入价格、日期、房型");
                c.getLayoutDayPrices().forEach(g->{
                    long roomCount = c.getRoomIds().stream().filter(e->e.getLayoutId().equals(g.getRoomLayoutId())).count();
                    if(roomCount > 0 ) {
                        g.setBookingOrderId(item.getOrderInfo().getId());
                        g.setBookingType(BookingOrdersType.TEAM.getKey());
                        g.setBatchId(batchItem.getId());
                    }
                });
                List<String> lList = c.getRoomIds().stream().map(a->a.getLayoutId()).collect(Collectors.toList());
                dayPriceService.saveBatch(c.getLayoutDayPrices().stream().filter(x->lList.contains(x.getRoomLayoutId())).collect(Collectors.toList()));
                //批次中处理每天房型价格 End
            });
            // 改变房间状态
            roomsService.getReserveRoom();
            return item.getOrderInfo().getBookingOrdersNo();
        } else { // not exist type
            throw new JeecgBootException("参数错误");
        }
    }

    public void addBusTeams(String hotelId, String bookingId){
        BusTeam team = new BusTeam();
        List<BusTeam> existTeams = teamService.list(Wrappers.<BusTeam>query().eq("is_delete", 0).eq("hotel_id", hotelId)
                .eq("rel_type",2)); // 1团队房，2联房
        Integer sortIndex = 1;
        List<Integer> indexs = existTeams.stream().map(s->s.getSortIndex()).collect(Collectors.toList());
        while(true){
            Integer finalSortIndex = sortIndex;
            if(indexs.stream().filter(s->s.equals(finalSortIndex)).count() == 0) {
                break;
            }
            sortIndex++;
        }
        team.setBookingOrderId(bookingId);
        team.setIsDelete(0);
        team.setHotelId(hotelId);
        team.setSortIndex(sortIndex);
        team.setRelType(2);

        teamService.save(team);
    }

    /**
     * 通过id获取预约单详情
     * @param orderId
     * @return
     */
    @Override
    public BookingOrderEditVo getBookingInfoById(String orderId,String orderNo) {
        BusRoomBookingOrders orderInfo;
        if(orderId != null && !orderId.isEmpty())
            orderInfo= getById(orderId);
        else if(orderNo != null && !orderNo.isEmpty())
            orderInfo = getOne(Wrappers.<BusRoomBookingOrders>query().eq("booking_orders_no",orderNo));
        else throw new JeecgBootException("参数错误");
        if(orderInfo == null) throw new JeecgBootException("预约单不存在");
        orderId = orderInfo.getId();
        BookingOrderEditVo result = new BookingOrderEditVo();
        if(orderInfo.getBookingOrdersType().equals(BookingOrdersType.NORMAL.getKey())) {
            List<ExtendBusBookingRoomsVo> bookingRooms = bookingRoomsService.bookingRooms(orderId,BookingOrdersType.NORMAL.getKey());

            List<BusBookingLayoutDayPrice> layoutDayPrices = dayPriceService.list(Wrappers.<BusBookingLayoutDayPrice>query()
            .eq("booking_order_id",orderId));
            bookingRooms.forEach(s->{
                List<BusBookingLayoutDayPrice> findLayoutPrice = layoutDayPrices.stream().filter(a->a.getRoomLayoutId().equals(s.getRoomLayoutId())).collect(Collectors.toList());
                s.setLayoutDayPrices(findLayoutPrice);
            });
            if (ObjectUtil.isNotEmpty(orderInfo.getBookingStatus()) && orderInfo.getBookingStatus() == 2){
                result.setLayoutDayPrices(new ArrayList<>());
                // 设置不是住脏或者住净的 预约中的房间
                result.setRoomIds(new ArrayList<>());
            } else {
                result.setLayoutDayPrices(layoutDayPrices);
                // 设置不是住脏或者住净的 预约中的房间
                result.setRoomIds(bookingRooms.stream().filter(r->!(r.getRoomStatus().equals(RoomStatusEnum.LIVE_DIRTY.getKey()) || r.getRoomStatus().equals(RoomStatusEnum.LIVE_CLEAR.getKey()))).collect(Collectors.toList()));
            }
            //<editor-fold msg="入住相关信息查询">
            List<ExtendBusBookingRoomsVo> livingRooms = bookingRooms.stream().filter(r->(r.getRoomStatus().equals(RoomStatusEnum.LIVE_DIRTY.getKey()) || r.getRoomStatus().equals(RoomStatusEnum.LIVE_CLEAR.getKey()))).collect(Collectors.toList());
            List<String> livingBookingRoomIds = livingRooms.stream().map(r->r.getId()).collect(Collectors.toList());
            List<BusRoomsLivingOrder> livingOrders =
                    livingBookingRoomIds.size() > 0?
                    roomsLivingOrderService.list(Wrappers.<BusRoomsLivingOrder>query()
                    .eq("booking_order_id",orderId)): new ArrayList<>();
            List<String> livingOrderIds = livingOrders.stream().map(r->r.getId()).collect(Collectors.toList());
            List<BusLivingCustomerVo> livingCustomers = livingOrderIds.size()>0? livingCustomerService.queryCustomVoList(livingOrderIds):new ArrayList<>();
            livingRooms.forEach(r->{
                Optional<BusRoomsLivingOrder> opLvOrder = livingOrders.stream().filter(a->a.getBookingRoomId().equals(r.getId())).findFirst();

                if(opLvOrder.isPresent()){
                    Optional<ExtendBusBookingRoomsVo> opBookingRoomsVo = bookingRooms.stream().filter(b->b.getIsMain().equals(true) && b.getId().equals(opLvOrder.get().getBookingRoomId())).findFirst();
                    if(opBookingRoomsVo.isPresent() &&
                            (opLvOrder.get().getSettleType().equals(SettleTypeEnum.SETTLE_LEAVE.getKey()) || opLvOrder.get().getSettleType().equals(SettleTypeEnum.UN_SETTLE_LEAVE.getKey()))) {
                        orderInfo.setSettleType(opLvOrder.get().getSettleType());
                    }
                    BusRoomsLivingOrder order = opLvOrder.get();
                    if (ObjectUtil.isNotEmpty(order.getArrivalTime())&& ObjectUtil.isNotEmpty(order.getDueOutTime())){
                        long millisDiff = order.getDueOutTime().getTime() - order.getArrivalTime().getTime();

                        // 将毫秒数差值转换为天数
                        int daysBetween = (int) TimeUnit.DAYS.convert(millisDiff, TimeUnit.MILLISECONDS) + 1;
                        order.setDayCount(daysBetween);
                    }
                    List<BusLivingCustomerVo> currentLivingCustomers = livingCustomers.stream().filter(s->s.getLivingOrderId().equals(order.getId())).collect(Collectors.toList());
                    List<BusLivingLayoutDayPrice> lvDayPrices = livingLayoutDayPriceService.list(Wrappers.<BusLivingLayoutDayPrice>query().eq("living_order_id",order.getId()));
                    r.setLivingOrder(order);
                    r.setLivingDayPrices(lvDayPrices);
                    r.setLivingCustomers(currentLivingCustomers);
                }
            });
            result.setLivingRoomIds(livingRooms);
            //</editor-fold>
            BusTeam team = teamService.getOne(Wrappers.<BusTeam>query().eq("booking_order_id",orderId).eq("is_delete",0).last("limit 1"));
            if(team != null) {
                result.setTeamTag(team.getSortIndex());
            }
        } else if(orderInfo.getBookingOrdersType().equals(BookingOrdersType.TEAM.getKey())) {
            List<BusBookingBatch> batches = bookingBatchService.list(Wrappers.<BusBookingBatch>query().eq("booking_orders_id",orderId));
            List<BookingBatchRoomsVo> batchesVo = new ArrayList<>();
            batches.forEach(s->{
                batchesVo.add(copyBaseToBatch(s));
            });
            List<ExtendBusBookingRoomsVo> batchRooms = bookingRoomsService.bookingRooms(orderId,BookingOrdersType.TEAM.getKey());
            List<BusBookingLayoutDayPrice> layoutDayPrices = dayPriceService.list(Wrappers.<BusBookingLayoutDayPrice>query()
            .eq("booking_order_id",orderId)
            .eq("booking_type",BookingOrdersType.TEAM.getKey()));
            batchesVo.forEach(s->{
                List<ExtendBusBookingRoomsVo> findRooms = batchRooms.stream().filter(a->a.getBookingBatchId().equals(s.getId())).collect(Collectors.toList());
                List<BusBookingLayoutDayPrice> findDayPrice = layoutDayPrices.stream().filter(a->a.getBatchId().equals(s.getId())).collect(Collectors.toList());
                findRooms.forEach(f->{
                    List<BusBookingLayoutDayPrice> batchPriceOfLayout = findDayPrice.stream().filter(i->i.getRoomLayoutId().equals(f.getRoomLayoutId())).collect(Collectors.toList());
                    f.setLayoutDayPrices(batchPriceOfLayout);
                });
                s.setLayoutDayPrices(findDayPrice);
                s.setRoomIds(findRooms);
            });
            result.setBatchRooms(batchesVo);
        }
        result.setOrderInfo(orderInfo);
        return result;
    }

    /**
     * 修改预约单
     * @param item
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean editBooking(BookingOrderEditVo item) {
        BusRoomBookingOrders order = getById(item.getOrderInfo().getId());
        if(order == null) throw new JeecgBootException("预约单不存在");

        if(item.getOrderInfo().getCustomerSource() == null || item.getOrderInfo().getCustomerSource().isEmpty())
            throw new JeecgBootException("客人来源不能为空");
        if(item.getOrderInfo().getCustomerType() == null)
            throw new JeecgBootException("客人类型不能为空");
        if(CustomerTypeEnum.val(item.getOrderInfo().getCustomerType()) == null)
            throw new JeecgBootException("客人类型不正确");
        if(item.getOrderInfo().getCustomerType().equals(CustomerTypeEnum.VIP.getKey())
                && (item.getOrderInfo().getVipCustomerId() == null || item.getOrderInfo().getVipCustomerId().isEmpty()))
            throw new JeecgBootException("客人类型为会员时，请选择会员");
        if(item.getOrderInfo().getCustomerType().equals(CustomerTypeEnum.PROTOCOL.getKey())
                && (item.getOrderInfo().getContactId() == null || item.getOrderInfo().getContactId().isEmpty()
                || item.getOrderInfo().getContractTeamProtocolId() == null || item.getOrderInfo().getContractTeamProtocolId().isEmpty()
        )
        )
            throw new JeecgBootException("客人类型为协议单位时，请选择协议单位和相关协议");
        if(item.getOrderInfo().getCustomerType().equals(CustomerTypeEnum.PROXY.getKey())
                && (item.getOrderInfo().getAgencyName() == null || item.getOrderInfo().getAgencyName().isEmpty())
        )
            throw new JeecgBootException("客人类型为中介时，请填写中介信息");
        // 钟点房判断，计费项目判定
        if(item.getOrderInfo().getBookingType().equals(CheckInTypeEnum.HOUR_TIME.getKey())) {
            if(item.getOrderInfo().getHourRoomId() == null || item.getOrderInfo().getHourRoomId().isEmpty())
                throw new JeecgBootException("钟点房计费项目不能为空");
        }
        // 【添加客人信息 Start】
        LoginUser user = TokenUtils.getAuthUser();
        if(item.getContactName() != null && !item.getContactName().isEmpty() && item.getPhone() != null && !item.getPhone().isEmpty()){
            BusCustomer customer = customerService.getOne(Wrappers.<BusCustomer>query().eq("name",item.getContactName())
                            .eq("hotel_id",item.getOrderInfo().getHotelId())
                    .eq("phone",item.getPhone()));
            if(customer == null) {
                // save customer
                BusCustomer newCustomer = new BusCustomer();
                newCustomer.setCreateTime(new Date());
                newCustomer.setCreateUser(user.getId());
                newCustomer.setHotelId(item.getOrderInfo().getHotelId());
                newCustomer.setTenantId(user.getRelTenantIds());
                newCustomer.setName(item.getContactName());
                newCustomer.setPhone(item.getPhone());
                newCustomer.setCertType(1); // 身份证
                customerService.save(newCustomer);
                item.getOrderInfo().setContactId(newCustomer.getId());
            } else {
                item.getOrderInfo().setContactId(customer.getId());
            }
        } else throw new JeecgBootException("客人信息不能为空");

        // 【添加客人信息 end】
        // 散客预定
        if(item.getOrderInfo().getBookingOrdersType().equals(BookingOrdersType.NORMAL.getKey())) {
            if(item.getOrderInfo().getArrivalTime() == null || item.getOrderInfo().getDueOutTime() == null)
                throw new JeecgBootException("预抵时间和预离时间不能为空");
            if(item.getOrderInfo().getArrivalTime().getTime() < new Date().getTime())
                throw new JeecgBootException("预抵时间不能小于当前时间");
            if(item.getOrderInfo().getArrivalTime().getTime() >= item.getOrderInfo().getDueOutTime().getTime())
                throw new JeecgBootException("预抵时间不能大于或者等于预离时间");
            // 非钟点房判定
            if(!item.getOrderInfo().getBookingType().equals(CheckInTypeEnum.HOUR_TIME.getKey())) {
                if(item.getOrderInfo().getDayCount() == null || item.getOrderInfo().getDayCount() <= 0)
                    throw new JeecgBootException("入住天数不能小于0");
            }

            if(item.getRoomIds() == null || item.getRoomIds().size() == 0)
                throw new JeecgBootException("请选择预定房间");
            item.getOrderInfo().setCreateTime(order.getCreateTime());
            item.getOrderInfo().setCreateUser(order.getCreateUser());
            item.getOrderInfo().setUpdateTime(new Date());

            item.getOrderInfo().setUpdateUser(user.getId());
            updateById(item.getOrderInfo());
            String bookingId = item.getOrderInfo().getId();
            // todo 判定是否有安排冲突的房间
            List<String> roomIds = item.getRoomIds().stream().map(BusBookingRooms::getRoomId).collect(Collectors.toList());
            List<BusRoomBookingOrders> clashOrder = baseMapper.findBookingOrder(roomIds, item.getOrderInfo().getArrivalTime(), item.getOrderInfo().getDueOutTime());
            List<BusRoomBookingOrders> endClashOrder = clashOrder.stream().filter(e -> !StrUtil.equals(e.getId(), order.getId())).collect(Collectors.toList());
            if (!endClashOrder.isEmpty()){
                String arrivalTime = DateUtil.format(clashOrder.get(0).getArrivalTime(), "YYYY-MM-dd");
                String dueOutTime = DateUtil.format(clashOrder.get(0).getDueOutTime(), "YYYY-MM-dd");
                throw new JeecgBootException( "房间" + arrivalTime + "~" + dueOutTime + "有预约");
            }

            //这里不能直接删除  先查询预订单有没有入住记录，如果房间有入住，那么入住过的记录则不能删除
            List<BusRoomsLivingOrder> bookingLivings = roomsLivingOrderService.list(Wrappers.<BusRoomsLivingOrder>query()
            .eq("booking_order_id",item.getOrderInfo().getId()));
            // 先找到已定的房间ROOMID
            List<String> livingRoomIds = new ArrayList<>();
            QueryWrapper bookingRoomDelWrapper = Wrappers.<BusBookingRooms>query()
                    .eq("booking_orders_id", item.getOrderInfo().getId());
            if(bookingLivings.size() > 0) {
                bookingRoomDelWrapper.notIn("id",bookingLivings.stream().map(g->g.getBookingRoomId()).collect(Collectors.toList()));
                livingRoomIds = bookingRoomsService.list(Wrappers.<BusBookingRooms>query().in("id",bookingLivings.stream().map(g->g.getBookingRoomId()).collect(Collectors.toList())))
                        .stream().map(g->g.getRoomId()).collect(Collectors.toList());
            }

            bookingRoomsService.remove(bookingRoomDelWrapper); // 这里将不会影响入住的房间
            bookingBatchService.remove(Wrappers.<BusBookingBatch>query().eq("booking_orders_id",item.getOrderInfo().getId()));
            dayPriceService.remove(Wrappers.<BusBookingLayoutDayPrice>query().eq("booking_order_id",item.getOrderInfo().getId()));
            List<BusBookingRooms> bookingRooms = new ArrayList<>();
            long mainCount = item.getRoomIds().stream().filter(t->t.getIsMain()!=null&&t.getIsMain()).count();
            List<String> finalLivingRoomIds = livingRoomIds;
            item.getRoomIds().forEach(c->{
                // 如果有房间入住了 就不添加了
                if(finalLivingRoomIds.contains(c.getRoomId())) return;
                BusBookingRooms bookingRoomsItem = new BusBookingRooms();
                bookingRoomsItem.setBookingOrdersId(bookingId);
                if(c.getRoomId() != null && !c.getRoomId().isEmpty()){
                    bookingRoomsItem.setRoomStatus(2);
                } else {
                    bookingRoomsItem.setRoomStatus(1);
                }
                if(mainCount != 1) {
                    bookingRoomsItem.setIsMain(false);
                } else {
                    bookingRoomsItem.setIsMain(c.getIsMain());
                }
                bookingRoomsItem.setRoomId(c.getRoomId());
                if(c.getRoomLayoutId() != null && !c.getRoomLayoutId().isEmpty()) {
                    bookingRoomsItem.setRoomLayoutId(c.getRoomLayoutId());
                } else bookingRoomsItem.setRoomLayoutId(c.getLayoutId());
                bookingRoomsItem.setRoomLayoutId(c.getLayoutId());
                bookingRoomsItem.setBookingType(1);
                bookingRooms.add(bookingRoomsItem);
            });
            if(mainCount != 1) {
                bookingRooms.get(0).setIsMain(true);
            }
            bookingRoomsService.saveBatch(bookingRooms);
            // 处理预定每天的房型价格 Start
            if(item.getLayoutDayPrices() == null || item.getLayoutDayPrices().size() == 0)
                throw new JeecgBootException("参数错误，对应房型当天价格必传");
            if(item.getLayoutDayPrices().stream().anyMatch(o->o.getPrice()==null || o.getDayTime() == null || o.getRoomLayoutId() == null))
                throw new JeecgBootException("参数错误，请传入价格、日期、房型");
            item.getLayoutDayPrices().forEach(g->{
                g.setId(null);
                g.setBookingOrderId(item.getOrderInfo().getId());
                g.setBookingType(BookingOrdersType.NORMAL.getKey());
            });
            List<String> layoutIds = item.getRoomIds().stream().map(v->v.getRoomLayoutId()).collect(Collectors.toList());
            dayPriceService.saveBatch(item.getLayoutDayPrices().stream().filter(v->layoutIds.contains(v.getRoomLayoutId())).collect(Collectors.toList()));
            // 处理预定每天的房型价格 End
            return true;
            // 团队预定
        } else if(item.getOrderInfo().getBookingOrdersType().equals(BookingOrdersType.TEAM.getKey())) {
            if(item.getOrderInfo().getTeamName() == null || item.getOrderInfo().getTeamName().isEmpty())
                throw new JeecgBootException("请填写团队名称");
            if(item.getBatchRooms() == null || item.getBatchRooms().size() == 0)
                throw  new JeecgBootException("请添加批次信息");
            updateById(item.getOrderInfo());
            bookingRoomsService.remove(Wrappers.<BusBookingRooms>query().eq("booking_orders_id", item.getOrderInfo().getId()));
            bookingBatchService.remove(Wrappers.<BusBookingBatch>query().eq("booking_orders_id",item.getOrderInfo().getId()));
            dayPriceService.remove(Wrappers.<BusBookingLayoutDayPrice>query().eq("booking_order_id",item.getOrderInfo().getId()));
            item.getBatchRooms().forEach(c->{
                c.setId(null);
                if(c.getArrivalTime() == null || c.getDueOutTime() == null)
                    throw new JeecgBootException("预抵时间和预离时间不能为空");
                if(c.getArrivalTime().getTime() < new Date().getTime())
                    throw new JeecgBootException("预抵时间不能小于当前时间");
                if(c.getArrivalTime().getTime() >= c.getDueOutTime().getTime())
                    throw new JeecgBootException("预抵时间不能大于或者等于预离时间");
                // 非钟点房判定
                if(!item.getOrderInfo().getBookingType().equals(CheckInTypeEnum.HOUR_TIME.getKey())) {
                    if(c.getDayCount() == null || c.getDayCount() <= 0)
                        throw new JeecgBootException("入住天数不能小于0");
                }
                setBatchOrderInfoForEdit(item.getOrderInfo(),c);
                if(c.getRoomIds() == null || c.getRoomIds().size() == 0)
                    throw new JeecgBootException("请选择批次房间");
                SimpleDateFormat sdfPc = new SimpleDateFormat("yyyyMMddHHmmssSSS");
                c.setBatchNo("YDPC"+sdfPc.format(System.currentTimeMillis()));
                c.setBookingOrdersId(item.getOrderInfo().getId());
                BusBookingBatch batchItem = c;
                bookingBatchService.save(batchItem);
                List<BusBookingRooms> batchBookingRooms = new ArrayList<>();
                long mainCount = c.getRoomIds().stream().filter(t->t.getIsMain()!=null&&t.getIsMain()).count();
                c.getRoomIds().forEach(s-> {
                    BusBookingRooms bookingRoomItem = new BusBookingRooms();
                    bookingRoomItem.setRoomId(s.getRoomId());
                    if(s.getRoomId() != null && !s.getRoomId().isEmpty()){
                        bookingRoomItem.setRoomStatus(2); // 未排房
                    } else {
                        bookingRoomItem.setRoomStatus(1); // 已排房
                    }
                    if(mainCount != 1) {
                        bookingRoomItem.setIsMain(false);
                    } else {
                        bookingRoomItem.setIsMain(s.getIsMain());
                    }
                    if(s.getRoomLayoutId() != null && !s.getRoomLayoutId().isEmpty()) {
                        bookingRoomItem.setRoomLayoutId(s.getRoomLayoutId());
                    } else bookingRoomItem.setRoomLayoutId(s.getLayoutId());
                    bookingRoomItem.setBookingType(2);
                    bookingRoomItem.setBookingBatchId(batchItem.getId());
                    bookingRoomItem.setBookingOrdersId(item.getOrderInfo().getId());
                    batchBookingRooms.add(bookingRoomItem);
                });
                if(mainCount != 1) {
                    batchBookingRooms.get(0).setIsMain(true);
                }
                bookingRoomsService.saveBatch(batchBookingRooms);
                // 以上处理批次关联的房间

                //批次中处理每天房型价格 Start
                if(c.getLayoutDayPrices() == null || c.getLayoutDayPrices().size() == 0)
                    throw new JeecgBootException("参数错误，批次中对应房型当天价格必传");
                if(c.getLayoutDayPrices().stream().anyMatch(o->o.getPrice()==null || o.getDayTime() == null || o.getRoomLayoutId() == null))
                    throw new JeecgBootException("参数错误，批次中请传入价格、日期、房型");
                c.getLayoutDayPrices().forEach(g->{
                        g.setBookingOrderId(item.getOrderInfo().getId());
                        g.setBookingType(BookingOrdersType.TEAM.getKey());
                        g.setBatchId(batchItem.getId());
                });
                List<String> layoutIds = c.getRoomIds().stream().map(v->v.getRoomLayoutId()).collect(Collectors.toList());
                dayPriceService.saveBatch(c.getLayoutDayPrices().stream().filter(p->layoutIds.contains(p.getRoomLayoutId())).collect(Collectors.toList()));
                //批次中处理每天房型价格 End
            });
            return true;
        } else { // not exist type
            throw new JeecgBootException("参数错误");
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean removeOrder(String id) {
        BusRoomBookingOrders order = getById(id);
        if(order == null) throw
             new JeecgBootException("订单不存在");
        removeById(id);
        bookingBatchService.remove(Wrappers.<BusBookingBatch>query().eq("booking_orders_id",order.getId()));
        // 找出预约单的 房间预定关联表 删除关联表，房间如果房间是预约状态，就变成上一个状态
        List<BusBookingRooms> busBookingRoomList = bookingRoomsService.list(Wrappers.<BusBookingRooms>query().eq("booking_orders_id", order.getId()));
        bookingRoomsService.removeBatchByIds(busBookingRoomList);
        List<String> roomIds = busBookingRoomList.stream().map(BusBookingRooms::getRoomId).collect(Collectors.toList());
        roomsService.update(Wrappers.<CesRooms>update().setSql("room_status = last_room_status")
                .eq("room_status",RoomStatusEnum.RESERVE.getKey()).in("id",roomIds));
        dayPriceService.remove(Wrappers.<BusBookingLayoutDayPrice>query().eq("booking_order_id",order.getId()));
        return true;
    }

    @Override
    public Boolean setOrderStatus(BusRoomBookingOrders entity) {
        if(entity.getId() == null || entity.getBookingStatus() == null) throw new JeecgBootException("参数错误");
        if(BookingStatusTypeEnum.val(entity.getBookingStatus()) == null) throw new JeecgBootException("状态错误");
        LambdaUpdateWrapper<BusRoomBookingOrders> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.set(BusRoomBookingOrders::getBookingStatus, entity.getBookingStatus());
        updateWrapper.eq(BusRoomBookingOrders::getId, entity.getId());
        update(updateWrapper);
        if (BookingStatusTypeEnum.CANCEL.getKey().equals(entity.getBookingStatus())){
            List<BusBookingRooms> bookingRoomsList = bookingRoomsService.list(Wrappers.<BusBookingRooms>lambdaQuery().eq(BusBookingRooms::getBookingOrdersId, entity.getId()));
            List<String> roomIds = bookingRoomsList.stream().map(BusBookingRooms::getRoomId).collect(Collectors.toList());
            roomsService.update(Wrappers.<CesRooms>update().setSql("room_status = last_room_status")
                    .eq("room_status",RoomStatusEnum.RESERVE.getKey()).in("id",roomIds));
        }
        return true;
    }

    @Override
    public BatchOrderEditVo batchOrderDetail(String batchId) {
        BusBookingBatch batch = bookingBatchService.getById(batchId);
        if(batch == null) throw new JeecgBootException("订单批次不存在");
        BookingOrderEditVo orderVo =  getBookingInfoById(batch.getBookingOrdersId(),null);
        BatchOrderEditVo result = new BatchOrderEditVo();
        List<BookingBatchRoomsVo> orderBatchVos = orderVo.getBatchRooms();
        if(orderBatchVos==null||orderBatchVos.size() == 0
        || orderBatchVos.stream().filter(s->s.getId().equals(batchId)).count()== 0
        ) throw new JeecgBootException("批次关联订单异常,请联系管理员");
        BookingBatchRoomsVo findBatchVo = orderBatchVos.stream().filter(s->s.getId().equals(batchId)).findAny().get();
        result.setOrderInfo(findBatchVo);
        result.setRoomIds(findBatchVo.getRoomIds());
        result.setLayoutDayPrices(findBatchVo.getLayoutDayPrices());
        return result;
    }

    /**
     * 修改或新增预定批次
     * @param item
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public String batchOrderSave(BatchOrderSavaDto item) {
        boolean isUpdate = item.getOrderInfo().getId() != null && !item.getOrderInfo().getId().isEmpty();
        BusBookingBatch batchInfo = item.getOrderInfo();
        if(isUpdate){
            if(batchInfo.getBatchNo() == null || batchInfo.getBatchNo().isEmpty())
                throw new JeecgBootException("修改请传入BatchNo");
        }
        LoginUser user = TokenUtils.getAuthUser();
        if(item == null || item.getOrderInfo() == null || item.getOrderInfo().getBookingOrdersId() == null) throw
            new JeecgBootException("参数错误");
        BusRoomBookingOrders orignOrder = getById(item.getOrderInfo().getBookingOrdersId());
        if(orignOrder == null) throw
            new JeecgBootException("关联订单信息不存在");
        item.getOrderInfo().setCreateTime(new Date());
        item.getOrderInfo().setCreateUser(user.getId());

        if(!isUpdate){
            SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS");
            item.getOrderInfo().setBatchNo("YDPC"+sdf.format(System.currentTimeMillis()));
        }
//        if(item.getOrderInfo().getBookingType() == null)

        if(item.getOrderInfo().getCustomerSource() == null || item.getOrderInfo().getCustomerSource().isEmpty())
            throw new JeecgBootException("客人来源不能为空");
        if(item.getOrderInfo().getCustomerType() == null)
            throw new JeecgBootException("客人类型不能为空");
        if(CustomerTypeEnum.val(item.getOrderInfo().getCustomerType()) == null)
            throw new JeecgBootException("客人类型不正确");
        if(item.getOrderInfo().getCustomerType().equals(CustomerTypeEnum.VIP.getKey())
                && (item.getOrderInfo().getVipCustomerId() == null || item.getOrderInfo().getVipCustomerId().isEmpty()))
            throw new JeecgBootException("客人类型为会员时，请选择会员");
        if(item.getOrderInfo().getCustomerType().equals(CustomerTypeEnum.PROTOCOL.getKey())
                && (item.getOrderInfo().getContactId() == null || item.getOrderInfo().getContactId().isEmpty()
                || item.getOrderInfo().getContractTeamProtocolId() == null || item.getOrderInfo().getContractTeamProtocolId().isEmpty()
        )
        )
            throw new JeecgBootException("客人类型为协议单位时，请选择协议单位和相关协议");
        if(item.getOrderInfo().getCustomerType().equals(CustomerTypeEnum.PROXY.getKey())
                && (item.getOrderInfo().getAgencyName() == null || item.getOrderInfo().getAgencyName().isEmpty())
        )
            throw new JeecgBootException("客人类型为中介时，请填写中介信息");
        // 钟点房判断，计费项目判定
        if(item.getOrderInfo().getBookingType().equals(CheckInTypeEnum.HOUR_TIME.getKey())) {
            if(item.getOrderInfo().getHourRoomId() == null || item.getOrderInfo().getHourRoomId().isEmpty())
                throw new JeecgBootException("钟点房计费项目不能为空");
        }

        // 如果是删除的话  先移除批次订单的关联房间和房型每天房价
        // 删除这块，后续的所有与预定相关的信息均不能与 预定的关联子表进行主键关联
        if(isUpdate) {
            bookingRoomsService.remove(Wrappers.<BusBookingRooms>query().eq("booking_batch_id",batchInfo.getId()));
            dayPriceService.remove(Wrappers.<BusBookingLayoutDayPrice>query().eq("batch_id",batchInfo.getId()));
        }
        // 【添加客人信息 Start】
        if(item.getContactName() != null && !item.getContactName().isEmpty() && item.getPhone() != null && !item.getPhone().isEmpty()){
            BusCustomer customer = customerService.getOne(Wrappers.<BusCustomer>query().eq("name",item.getContactName())
                            .eq("hotel_id",item.getHotelId())
                    .eq("phone",item.getPhone()));
            if(customer == null) {
                // save customer
                BusCustomer newCustomer = new BusCustomer();
                newCustomer.setCreateTime(new Date());
                newCustomer.setCreateUser(user.getId());
                newCustomer.setHotelId(item.getHotelId());
                newCustomer.setTenantId(user.getRelTenantIds());
                newCustomer.setName(item.getContactName());
                newCustomer.setPhone(item.getPhone());
                newCustomer.setCertType(1); // 身份证
                customerService.save(newCustomer);
                item.getOrderInfo().setContactId(newCustomer.getId());
            } else {
                item.getOrderInfo().setContactId(customer.getId());
            }
        } else throw new JeecgBootException("客人信息不能为空");

        // 【添加客人信息 end】


        BusBookingBatch c = item.getOrderInfo();

//        setBatchOrderInfo(item.getOrderInfo(),c);
        if(c.getArrivalTime() == null || c.getDueOutTime() == null)
            throw new JeecgBootException("预抵时间和预离时间不能为空");
        if(c.getArrivalTime().getTime() < new Date().getTime())
            throw new JeecgBootException("预抵时间不能小于当前时间");
        if(c.getArrivalTime().getTime() >= c.getDueOutTime().getTime())
            throw new JeecgBootException("预抵时间不能大于或者等于预离时间");
        // 非钟点房判定
        if(!item.getOrderInfo().getBookingType().equals(CheckInTypeEnum.HOUR_TIME.getKey())) {
            if(c.getDayCount() == null || c.getDayCount() <= 0)
                throw new JeecgBootException("入住天数不能小于0");
        }
        if(item.getRoomIds() == null || item.getRoomIds().size() == 0)
            throw new JeecgBootException("请选择批次房间");
        SimpleDateFormat sdfPc = new SimpleDateFormat("yyyyMMddHHmmssSSS");
        c.setBatchNo("YDPC"+sdfPc.format(System.currentTimeMillis()));
        c.setBookingOrdersId(item.getOrderInfo().getBookingOrdersId());

//        BusBookingBatch batchItem = copyBatchToBase(c);
//        BusBookingBatch batchItem = copyBatchToBase(c);
        if(item.getOrderInfo().getId() != null && !item.getOrderInfo().getId().isEmpty()) {
            bookingBatchService.updateById(c);
        }
        else bookingBatchService.save(c);
        List<BusBookingRooms> batchBookingRooms = new ArrayList<>();
        long mainCount = item.getRoomIds().stream().filter(t->t.getIsMain()!=null&&t.getIsMain()).count();
        item.getRoomIds().forEach(s-> {
            BusBookingRooms bookingRoomItem = new BusBookingRooms();
            bookingRoomItem.setRoomId(s.getRoomId());
            bookingRoomItem.setRoomLayoutId(s.getLayoutId());
            if(s.getRoomId() != null && !s.getRoomId().isEmpty()){
                bookingRoomItem.setRoomStatus(2);
            } else {
                bookingRoomItem.setRoomStatus(1);
            }
            if(mainCount != 1) {
                bookingRoomItem.setIsMain(false);
            } else {
                bookingRoomItem.setIsMain(s.getIsMain());
            }
            bookingRoomItem.setBookingType(2);
            bookingRoomItem.setBookingBatchId(c.getId());
            bookingRoomItem.setBookingOrdersId(item.getOrderInfo().getBookingOrdersId());
            batchBookingRooms.add(bookingRoomItem);
        });
        if(mainCount != 1) {
            batchBookingRooms.get(0).setIsMain(true);
        }
        bookingRoomsService.saveBatch(batchBookingRooms);
        // 以上处理批次关联的房间

        //批次中处理每天房型价格 Start
        if(item.getLayoutDayPrices() == null || item.getLayoutDayPrices().size() == 0)
            throw new JeecgBootException("参数错误，批次中对应房型当天价格必传");
        if(item.getLayoutDayPrices().stream().anyMatch(o->o.getPrice()==null || o.getDayTime() == null || o.getRoomLayoutId() == null))
            throw new JeecgBootException("参数错误，批次中请传入价格、日期、房型");
        item.getLayoutDayPrices().forEach(g->{
            long roomCount = item.getRoomIds().stream().filter(e->e.getLayoutId().equals(g.getRoomLayoutId())).count();
            if(roomCount > 0 ) {
                g.setBookingOrderId(item.getOrderInfo().getBookingOrdersId());
                g.setBookingType(BookingOrdersType.TEAM.getKey());
                g.setBatchId(c.getId());
            }
        });
        List<String> lList = item.getRoomIds().stream().map(a->a.getLayoutId()).collect(Collectors.toList());
        dayPriceService.saveBatch(item.getLayoutDayPrices().stream().filter(x->lList.contains(x.getRoomLayoutId())).collect(Collectors.toList()));
        //批次中处理每天房型价格 End

        return c.getBatchNo();
    }

    /**
     * 设置批次预定状态
     * @param batchOrder
     * @return
     */
    @Override
    public Boolean setBatchOrderStatus(BusBookingBatch batchOrder) {
        if(batchOrder == null || batchOrder.getId() == null || batchOrder.getId().isEmpty()
        || BookingStatusTypeEnum.val(batchOrder.getBookingStatus()) == null
        )
            throw new JeecgBootException("参数错误");
        BusBookingBatch queryBatchOrder = bookingBatchService.getById(batchOrder.getId());
        if(queryBatchOrder == null) throw new JeecgBootException("订单批次不存在");
        queryBatchOrder.setBookingStatus(batchOrder.getBookingStatus());
        bookingBatchService.updateById(queryBatchOrder);
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String bookingToLive(String bookingRoomId, List<BusLivingCustomer> customers, String hotelId, Boolean bookingLiving,
                                BookingLayoutRoomsDto roomInfo, List<BusLivingLayoutDayPrice> livingRoomDayPrices) {
        BusBookingRooms bkRoom = bookingRoomsService.getById(bookingRoomId);
        if(bkRoom == null) throw  new JeecgBootException("关联房间不存在");
        BusRoomBookingOrders bookingOrders = getById(bkRoom.getBookingOrdersId());
        if(bookingOrders == null) throw new JeecgBootException("房间预约单不存在");
        LoginUser user = TokenUtils.getAuthUser();
        //<editor-fold desc="订单复制 -- 散客入住">
        BusRoomsLivingOrder livingOrder = null;
        if(bkRoom.getBookingType().equals(BookingOrdersType.NORMAL.getKey())) {
            //todo other
            if(roomInfo.getRoomOrderInfo() != null) {
                roomInfo.getRoomOrderInfo().setId(bookingOrders.getId());
                // 设置为当前时间
                roomInfo.getRoomOrderInfo().setArrivalTime(new Date());
            } else {
                roomInfo.setRoomOrderInfo(bookingOrders);
            }
            livingOrder = copyBookingToAcceptOrder(roomInfo.getRoomOrderInfo());
            livingOrder.setBookingRoomId(bookingRoomId);
        } else if(bkRoom.getBookingType().equals(BookingOrdersType.TEAM.getKey())) {
            BusBookingBatch batch = bookingBatchService.getById(bkRoom.getBookingBatchId());
            if(batch == null) throw new JeecgBootException("批次不存在");
            livingOrder = copyBookingToAcceptOrder(batch, bookingOrders);
            livingOrder.setBatchId(bkRoom.getBookingBatchId());
        } else throw new JeecgBootException("订单类型错误");

        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS");
        livingOrder.setLivingOrderNo("D"+sdf.format(System.currentTimeMillis()));
        livingOrder.setHotelId(hotelId);

        livingOrder.setArrivalTime(new Date());// todo 这个地方的入住开始时间应该怎么算
        if(customers != null && !customers.isEmpty()) {
            BusCustomer customer = customerService.getOne(Wrappers.<BusCustomer>query().eq("name",customers.get(0).getCustomerName())
                    .eq("phone",customers.get(0).getPhone()));
            if (customer == null){
                long id = new Snowflake().nextId();
                customers.get(0).setCustomerId(StrUtil.toString(id));
            } else {
                customers.get(0).setCustomerId(customer.getId());
            }
            livingOrder.setContactId(customers.get(0).getCustomerId());
        } else throw new JeecgBootException("请添加入住人信息");
        roomsLivingOrderService.save(livingOrder);
        // 添加查房记录 start
        FwRoomExamine examine = new FwRoomExamine();
        examine.setHotelId(hotelId);
        examine.setCreateTime(new Date());
        examine.setLivingOrderId(livingOrder.getId());
        examine.setRoomId(bkRoom.getRoomId());
        fwRoomExamineService.save(examine);
        // 添加查房记录 end

        CesRooms room = roomsService.getById(bkRoom.getRoomId());
        if(room==null) throw new JeecgBootException("房间不存在");
        room.setLivingOrderId(livingOrder.getId());
        Integer roomStatus = roomsService.changeLivingRoomStatus(room.getRoomStatus(), room.getLastRoomStatus());
        room.setLastRoomStatus(room.getRoomStatus());
        room.setRoomStatus(roomStatus);
        room.setWaterMeter(roomInfo.getWaterMeter());
        room.setEnergyMeter(roomInfo.getEnergyMeter());
        room.setGasMeter(roomInfo.getGasMeter());
        roomsService.updateById(room);
        bkRoom.setRoomStatus(RoomStatusEnum.LIVE_DIRTY.getKey());
        bookingRoomsService.updateById(bkRoom);
        //</editor-fold>

        //<editor-fold desc="同住人相关">

        BusRoomsLivingOrder finalLivingOrder = livingOrder;
        customers.forEach(s->{
            // 【添加客人信息 Start】
            if(s.getCustomerName() != null && !s.getCustomerName().isEmpty() && s.getPhone() != null && !s.getPhone().isEmpty()){
                BusCustomer customer = customerService.getOne(Wrappers.<BusCustomer>query().eq("name",s.getCustomerName())
                                .eq("hotel_id",hotelId)
                        .eq("phone",s.getPhone()));
                if(customer == null) {
                    // save customer
                    BusCustomer newCustomer = new BusCustomer();
                    // 新顾客第一个会先自定义一个id，因为上面livingOrder要关联顾客id
                    newCustomer.setId(s.getCustomerId());
                    newCustomer.setCreateTime(new Date());
                    newCustomer.setCreateUser(user.getRealname());
                    newCustomer.setHotelId(hotelId);
                    newCustomer.setTenantId(user.getRelTenantIds());
                    newCustomer.setName(s.getCustomerName());
                    newCustomer.setGender(s.getGender());
                    newCustomer.setAddress(s.getAddress());
                    newCustomer.setPhone(s.getPhone());
                    newCustomer.setCertType(1); // 身份证
                    newCustomer.setCertNo(s.getCertNo()); // 身份证
                    customerService.save(newCustomer);
                    s.setCustomerId(newCustomer.getId());
                } else {
                    s.setCustomerId(customer.getId());
                }
            } else throw new JeecgBootException("客人信息不能为空");
            s.setLivingOrderId(finalLivingOrder.getId());
            livingCustomerService.save(s);
            // 【添加客人信息 end】
        });

        //</editor-fold>

         //</editor-fold>

        //<editor-fold desc="todo:账单相关">
//        if(bookingLiving != null && bookingLiving) {

        feeService.addBillFee(livingOrder, room,roomInfo.getLongRentScheme(),livingRoomDayPrices);

//        }


        CesAllDayPriceRule cesAllDayPriceRule =  allDayPriceRuleService.getOne(Wrappers.<CesAllDayPriceRule>lambdaQuery().eq(CesAllDayPriceRule::getHotelId,hotelId).eq(CesAllDayPriceRule::getInvalid,false));
        if(ObjectUtils.isEmpty(cesAllDayPriceRule)) {
            throw new JeecgBootException("未找到超时收费规则");
        }
        if (ObjectUtil.equals(LivingTypeEnum.ALL_DAY.getKey(), livingOrder.getLivingType())){
            Calendar calendar = getRemindTime(livingOrder, cesAllDayPriceRule, roomInfo.getRoomOrderInfo().getBookingType());
            redisTemplate.opsForZSet().add("overTimerOrder", livingOrder.getId(), calendar.getTimeInMillis());
        } else if (ObjectUtil.equals(LivingTypeEnum.HOURS_RENT.getKey(), livingOrder.getLivingType())){
            Calendar calendar = Calendar.getInstance();
            calendar.setTime(livingOrder.getDueOutTime());
            calendar.add(Calendar.MINUTE, -30);
            redisTemplate.opsForZSet().add("sys:cache:message:willEndOrder", livingOrder.getId(), calendar.getTimeInMillis());
            redisTemplate.opsForZSet().add("overTimerOrder", livingOrder.getId(), livingOrder.getDueOutTime().getTime());
        } else if(ObjectUtil.equals(LivingTypeEnum.LONG_RENT.getKey(), livingOrder.getLivingType())){
            Calendar calendar = Calendar.getInstance();
            calendar.setTime(livingOrder.getDueOutTime());
            calendar.add(Calendar.DAY_OF_YEAR, -7);
            if (CollUtil.isEmpty(roomInfo.getLongRentScheme().getHouseLongRentChargeList())){
                throw new JeecgBootException("未找到长租房收费项");
            }
            Optional<CesHouseLongRentCharge> chargeOptional = roomInfo.getLongRentScheme().getHouseLongRentChargeList().stream().filter(e -> e.getChargeType() == 2 && e.getIsMust()).findFirst();
            if (!chargeOptional.isPresent()){
                throw new JeecgBootException("未找到房租费项");
            }
            Integer cycle = chargeOptional.get().getNum();
            Calendar nextCalendar = Calendar.getInstance();
            nextCalendar.setTime(livingOrder.getArrivalTime());
            calendar.setTime(livingOrder.getArrivalTime());
            calendar.add(Calendar.MONTH, cycle);

            log.info(DateUtil.format(calendar.getTime(),"yyyy-MM-dd"));
            redisTemplate.opsForZSet().add("overTimerOrder", livingOrder.getId(), calendar.getTimeInMillis());
            redisTemplate.opsForZSet().add("sys:cache:message:willEndOrder", livingOrder.getId(), calendar.getTimeInMillis());
        }
        roomsService.getReserveRoom();
        //</editor-fold>
        return livingOrder.getId();
    }

    /**
     * 首次超时收费时间
     */
    public Calendar getRemindTime(BusRoomsLivingOrder livingOrder, CesAllDayPriceRule cesAllDayPriceRule, Integer BookingType){
        Calendar calendar = Calendar.getInstance();
        if (ObjectUtil.equals(BookingType, 1)) {
            calendar.setTime(livingOrder.getArrivalTime());
            calendar.add(Calendar.DAY_OF_YEAR, 1);
            Calendar overDate = Calendar.getInstance();
            overDate.setTime(livingOrder.getDueOutTime());
            calendar.set(Calendar.HOUR_OF_DAY, overDate.get(Calendar.HOUR_OF_DAY));
            calendar.set(Calendar.MINUTE, overDate.get(Calendar.MINUTE));
            calendar.add(Calendar.MINUTE, Integer.parseInt(cesAllDayPriceRule.getDayTime()));
        }else if(ObjectUtil.equals(BookingType, 2)){
            // TODO: 2023/10/24 钟点房超时时间
            calendar.setTime(livingOrder.getDueOutTime());
        }
        return calendar;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String customerLiving(BookingOrderSaveDto livingData, Boolean isTeam, String hotelId) {
        // 查看房间入住情况
        List<String> roomIds = livingData.getRoomIds().stream().map(BookingLayoutRoomsDto::getRoomId).collect(Collectors.toList());
        List<CesRooms> rooms = roomsService.listByIds(roomIds);
        Optional<CesRooms> optional = rooms.stream().filter(e -> StrUtil.isNotEmpty(e.getLivingOrderId())).findFirst();
        if (optional.isPresent()){
            throw new JeecgBootException("房间已被入住");
        }
        // 查看房间预约情况
        List<BusRoomBookingOrders> clashOrder = baseMapper.findBookingOrder(roomIds, livingData.getOrderInfo().getArrivalTime(), livingData.getOrderInfo().getDueOutTime());
        long count = clashOrder.stream().filter(e -> !StrUtil.equals(e.getId(), livingData.getOrderInfo().getId())).count();
        if (count > 0) {
            throw new JeecgBootException("房间已被预约");
        }
        // 是预约单先将预约单与房间关联删掉。因为原来这个接口是自己入住的接口，后来预定也要调这个接口，只能先将预定时添加的BusRoomBookingOrders关联表删掉，重新进行关联
        if(StrUtil.isNotEmpty(livingData.getOrderInfo().getId())){
            BusRoomBookingOrders bookingOrders = getById(livingData.getOrderInfo().getId());
            bookingRoomsService.remove(Wrappers.<BusBookingRooms>lambdaQuery()
                    .eq(BusBookingRooms::getBookingOrdersId,bookingOrders.getId()).in(BusBookingRooms::getRoomId,roomIds));
        }
        //不是预约单就先添加预约单
        bookingOrderSave(livingData,isTeam,true,hotelId);
        String bookingOrderNo = livingData.getOrderInfo().getBookingOrdersNo();
        if(bookingOrderNo != null && !bookingOrderNo.isEmpty()){
            BookingOrderEditVo bookingOrderVo =  getBookingInfoById(null,bookingOrderNo);
            // 散客取roomIds
            if(bookingOrderVo.getOrderInfo().getBookingOrdersType().equals(BookingOrdersType.NORMAL.getKey())) {
                // 返回map中key为房间id, value为居住单id 。该方法添加房间，费用等入住信息，
                Map<String,String> roomOrderMap = bookingRoomToLiving(livingData, bookingOrderVo.getRoomIds(), bookingOrderVo,hotelId);
                livingData.getLivingRoomDayPrices().forEach(g->{
                    String livingOrderId = roomOrderMap.get(g.getRoomId());
                    if(livingOrderId != null && !livingOrderId.isEmpty()) {
                        g.setLivingOrderId(livingOrderId);
                        g.setLivingType(BookingOrdersType.NORMAL.getKey());
                    } else throw new JeecgBootException("房间订单错误");
                });
                livingLayoutDayPriceService.saveBatch(livingData.getLivingRoomDayPrices());
            } else if(bookingOrderVo.getOrderInfo().getBookingOrdersType().equals(BookingOrdersType.TEAM.getKey())) {
                bookingOrderVo.getBatchRooms().forEach(b->{
                    /*保存入住单，保存入住费用*/
                    bookingRoomToLiving(livingData, b.getRoomIds(), bookingOrderVo,hotelId);
                });
            } else throw new JeecgBootException("订单类型错误");
        }
        return bookingOrderNo;
    }

    @Override
    public List<FloorBuildingRoomVo> getRealtimeInfo(List<CesRooms> rooms) {
        List<FloorBuildingRoomVo> roomVos = new ArrayList<>();
//        List<String> bookingOrderIds = rooms.stream().map(s->s.getBookingOrderId()).collect(Collectors.toList());
        // 这里只获取当天预约的房间预约单，而非上面的那种情况，即：ces_rooms 表中的booking_order_id作废
        List<TodayBookingQueryDto> roomBookingOrders = baseMapper.getTodayBookingOrder(rooms.stream().map(s->s.getId()).collect(Collectors.toList()));
        List<String> bookingOrderIds = roomBookingOrders.stream().map(TodayBookingQueryDto::getBookingOrderId).collect(Collectors.toList());
        List<String> livingOrderIds = rooms.stream().map(CesRooms::getLivingOrderId).collect(Collectors.toList());
        List<BusRoomBookingOrders> bookingOrders = !bookingOrderIds.isEmpty() ?
                this.list(Wrappers.<BusRoomBookingOrders>query().in("id",bookingOrderIds))
                :new ArrayList<>();
        List<BusBookingLayoutDayPrice> bookingLayoutDayPrices = !bookingOrderIds.isEmpty() ? dayPriceService.list(Wrappers.<BusBookingLayoutDayPrice>query()
        .in("booking_order_id",bookingOrderIds)):new ArrayList<>();
        List<String> bookingCusIds = bookingOrders.stream().map(BusRoomBookingOrders::getContactId).collect(Collectors.toList());
        List<BusCustomer> bookingCustomers = !bookingCusIds.isEmpty() ? customerService.list(Wrappers.<BusCustomer>query()
        .in("id",bookingCusIds)):new ArrayList<>();
        List<BusRoomsLivingOrder> livingOrders = !livingOrderIds.isEmpty() ? roomsLivingOrderService.list(Wrappers.<BusRoomsLivingOrder>query()
        .in("id",livingOrderIds)):new ArrayList<>();
        List<BusLivingCustomer> livingCustomers = !livingOrderIds.isEmpty() ? livingCustomerService.list(Wrappers.<BusLivingCustomer>query()
        .in("living_order_id",livingOrderIds)):new ArrayList<>();
        // 每日房价
        List<BusLivingLayoutDayPrice> livingLayoutDayPrices = !livingOrderIds.isEmpty() ? livingLayoutDayPriceService.list(Wrappers.<BusLivingLayoutDayPrice>query()
        .in("living_order_id",livingOrderIds)):new ArrayList<>();
        Map<String, List<BusLivingLayoutDayPrice>> livingDayPriceMap = livingLayoutDayPrices.stream().collect(Collectors.groupingBy(BusLivingLayoutDayPrice::getLivingOrderId));
        List<CesHouseLongRentCharge> houseLongRentCharges = houseLongRentChargeService.list(Wrappers.<CesHouseLongRentCharge>lambdaQuery().in(CesHouseLongRentCharge::getLivingOrderId, livingOrderIds));
        List<BusTeam> teamOrders_all = teamService.list(Wrappers.<BusTeam>lambdaQuery().eq(BusTeam::getIsDelete, 0));
        List<String> booingIds = teamOrders_all.stream().map(BusTeam::getBookingOrderId).collect(Collectors.toList());
//        List<BusBookingRooms> bookingRoomsList = bookingRoomsService.bookingRoomsDetail(Wrappers.<BusBookingRooms>lambdaQuery().in(BusBookingRooms::getBookingOrdersId, booingIds));
        List<BusBookingRooms> bookingRoomsList = bookingRoomsService.bookingRoomsDetail(booingIds);
        List<BusOrderFee> fees = feeService.list(Wrappers.<BusOrderFee>query()
                .in("living_order_id",livingOrderIds));
        Map<String, List<BusOrderFee>> feesMap = fees.stream().collect(Collectors.groupingBy(BusOrderFee::getLivingOrderId));
        rooms.forEach(s-> {
            FloorBuildingRoomVo vo = new FloorBuildingRoomVo();
            vo.setRoomId(s.getId());
            vo.setBookingData(new BookingRealtimeVo());
            vo.setLivingData(new LivingRealtimeVo());
            // 通过房间id找出对应的预约单号， 实际上目前数据库房间表的预约订单id全部未空
            Optional<TodayBookingQueryDto> optionalTodayBookingQueryDto =  roomBookingOrders.stream().filter(t->t.getRoomId().equals(s.getId())).findFirst();
            if(optionalTodayBookingQueryDto.isPresent()) {
                s.setBookingOrderId(optionalTodayBookingQueryDto.get().getBookingOrderId());
                // 找出当前房间的预约单
                Optional<BusRoomBookingOrders> opBookingOrder = bookingOrders.stream().filter(a->a.getId().equals(s.getBookingOrderId())).findFirst();
                if(opBookingOrder.isPresent()) {
                    //设置团队联房标签
                    Optional<BusTeam> opTeam = teamOrders_all.stream().filter(r->r.getBookingOrderId().equals(opBookingOrder.get().getId())).findFirst();
                    if(opTeam.isPresent()) {
                        BusTeam t = opTeam.get();
                        if(t.getRelType().equals(1)) {
                            opBookingOrder.get().setIsTeam(true);
                            opBookingOrder.get().setTeamTag("团"+t.getSortIndex());
                        } else {
                            opBookingOrder.get().setIsRel(true);
                            opBookingOrder.get().setRelTag("联"+t.getSortIndex());
                        }
                        // 关联房间
                        List<BusBookingRooms> releBookingRooms = bookingRoomsList.stream().filter(e ->
                                opBookingOrder.get().getId().equals(e.getBookingOrdersId())).collect(Collectors.toList());
                        vo.getBookingData().setReleBookingRooms(releBookingRooms);
                    }

                    vo.getBookingData().setBookingOrder(opBookingOrder.get());
                    Optional<BusCustomer> opBookingCustomer = bookingCustomers.stream().filter(a->a.getId().equals(opBookingOrder.get().getContactId())).findFirst();
                    opBookingCustomer.ifPresent(customer -> vo.getBookingData().setBookingCustomer(customer));
                    vo.getBookingData().setLayoutDayPrices(bookingLayoutDayPrices.stream().filter(a->a.getBookingOrderId().equals(opBookingOrder.get().getId())).collect(Collectors.toList()));
                }
            }
            // 找出当前房间的居住单
             Optional<BusRoomsLivingOrder> opLivingOrder=livingOrders.stream().filter(a->a.getId().equals(s.getLivingOrderId())).findFirst();
             if(opLivingOrder.isPresent()) {
                 BusRoomsLivingOrder livingOrder = opLivingOrder.get();
                 Map<String,BigDecimal> feeMap = getRoomOrderFees(livingOrder.getId(), feesMap);
                 livingOrder.setXiaoFei(feeMap.get("xf"));
                 livingOrder.setShouKuan(feeMap.get("sk"));
                 livingOrder.setDeposit(feeMap.get("yj"));
                 // 获取入住天数
                 livingOrder.setLiveDayCount(getLivingDayCount(livingOrder));
                 // 设置居住单的团队联房标签
                 Optional<BusTeam> opTeam = teamOrders_all.stream().filter(r->r.getBookingOrderId().equals(livingOrder.getBookingOrderId())).findFirst();
                 if(opTeam.isPresent()) {
                     BusTeam t = opTeam.get();
                     if(t.getRelType().equals(1)) {
                         livingOrder.setIsTeam(true);
                         livingOrder.setTeamTag("团"+opTeam.get().getSortIndex());
                     } else {
                         livingOrder.setIsRel(true);
                         livingOrder.setRelTag("联"+opTeam.get().getSortIndex());
                     }
                     List<BusBookingRooms> releBookingRooms = bookingRoomsList.stream().filter(e ->
                             livingOrder.getBookingOrderId().equals(e.getBookingOrdersId())).collect(Collectors.toList());
                     vo.getBookingData().setReleBookingRooms(releBookingRooms);
                 }
                 // 赋值
                 vo.getLivingData().setLivingOrder(livingOrder);
                 Optional<BusLivingCustomer> opLivingCustomer = livingCustomers.stream().filter(a->a.getLivingOrderId().equals(opLivingOrder.get().getId())).findFirst();
                 opLivingCustomer.ifPresent(busLivingCustomer -> vo.getLivingData().setLivingCustomers(busLivingCustomer));
//                 vo.getLivingData().setPrice(
//                         livingLayoutDayPrices.stream().filter(a->a.getLivingOrderId().equals(opLivingOrder.get().getId())).collect(Collectors.toList()));
                 vo.getLivingData().setPrice(livingDayPriceMap.get(opLivingOrder.get().getId()));
                 List<CesHouseLongRentCharge> charges = houseLongRentCharges.stream().filter(charge -> livingOrder.getId().equals(charge.getLivingOrderId())).collect(Collectors.toList());
                 vo.getLivingData().setLongRentChargeList(charges);
             }
            roomVos.add(vo);
        });

        return roomVos;
    }


    private Integer getLivingDayCount(BusRoomsLivingOrder order) {
        Calendar calendarBeijian = new GregorianCalendar();
        calendarBeijian.setTime(order.getDueOutTime());
        Calendar calendarJian = new GregorianCalendar();
        calendarJian.setTime(order.getArrivalTime());
        int count = DateUtils.dateDiff('d',calendarBeijian,calendarJian);
       return  count == 0 ? 1:count;
    }

    private Map<String, BigDecimal> getRoomOrderFees(String livingOrderId, Map<String, List<BusOrderFee>> feesMap) {
        List<BusOrderFee> fees = feesMap.get(livingOrderId);
        if (fees.isEmpty()){
            return new HashMap<>();
        }
        List<BusOrderFee> xiaofeiList = fees.stream().filter(s->s.getFeeType().equals(1)).collect(Collectors.toList());
        List<BusOrderFee> shoukuanList = fees.stream().filter(s->s.getFeeType().equals(2)).collect(Collectors.toList());
        List<BusOrderFee> depositList = shoukuanList.stream().filter(s->ObjectUtil.equals(s.getSubjectType(),"1")).collect(Collectors.toList());
        final BigDecimal[] xiaoFeiTotal = {new BigDecimal(0)};
        final BigDecimal[] shouKuanTotal = {new BigDecimal(0)};
        final BigDecimal[] depositTotal = {new BigDecimal(0)};
        xiaofeiList.forEach(s->{
            xiaoFeiTotal[0] = xiaoFeiTotal[0].add(s.getMoney());
        });
        shoukuanList.forEach(s->{
            shouKuanTotal[0] = shouKuanTotal[0].add(s.getMoney());
        });
        depositList.forEach(s->{
            depositTotal[0] = depositTotal[0].add(s.getMoney());
        });
        Map<String,BigDecimal> items = new HashMap<>();
        items.put("xf",xiaoFeiTotal[0]);
        items.put("sk",shouKuanTotal[0]);
        items.put("yj",depositTotal[0]);
        return items;
    }

    @Override
    public List<BusOrderFee> getLivingOrderFee(String orderId, Boolean isAllFee) {
        List<BusRoomsLivingOrder> livingOrders = roomsLivingOrderService.list(Wrappers.<BusRoomsLivingOrder>query().eq("booking_order_id",orderId));
        if(!livingOrders.isEmpty()) {
            List<String> livingOrderIds = livingOrders.stream().map(BusRoomsLivingOrder::getId).collect(Collectors.toList());
            // 所有订单商品关联对象
            List<BusOrderFeeGoodsVo> busOrderFeeGoodVos = orderFeeGoodsService.queryOrderGoodDetail(livingOrderIds);
            // 所有订单
            LambdaQueryWrapper<BusOrderFee> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.in(BusOrderFee::getLivingOrderId, livingOrderIds);
            // 如果是null 则查询未结算的， 可以添加false为查询已结算的
            if (ObjectUtil.isEmpty(isAllFee)){
                queryWrapper.eq(BusOrderFee::getPreferentialStatus, 1);
            }
            List<BusOrderFee> orderFees = feeService.list(queryWrapper);
            List<String> readMeterFeeIds = orderFees.stream().filter(e -> ObjectUtil.equals(
                    e.getSubjectType(), FeeSubjectType.SHUI_DIAN_MEI.getKey())).map(BusOrderFee::getId).collect(Collectors.toList());
            List<CesHouseLongRentMeterLog> longRentMeterLogs;
            if (!readMeterFeeIds.isEmpty()){
                // 水电燃气读表日志
                longRentMeterLogs = houseLongRentMeterLogService.list(Wrappers
                        .<CesHouseLongRentMeterLog>lambdaQuery().in(CesHouseLongRentMeterLog::getOrderFeeId, readMeterFeeIds));
            } else {
                longRentMeterLogs = new ArrayList<>();
            }

            List<BusOrderFee> strikeBalanceFees = orderFees.stream().filter(e -> e.getReturnItem() && FeeType.CONSUME.getKey()
                    .equals(e.getFeeType())).collect(Collectors.toList());
            // 最终返回费用。当费用是商品消费时找出商品。消费的费用 需要处理掉 冲账的费用
            ArrayList<BusOrderFee> orderFeeList = new ArrayList<>();
            orderFees.forEach(e ->{
                // 订单为 收款单时
                if (FeeType.COLLECTION.getKey().equals(e.getFeeType())){
                    orderFeeList.add(e);
                    // 订单为 消费单，且不是冲账单
                } else if (!e.getReturnItem()){
                    BigDecimal strikeBalance = strikeBalanceFees.stream().filter(sbFee -> e.getId().equals(sbFee.getReturnFeeId()))
                            .map(BusOrderFee::getMoney).reduce(BigDecimal.ZERO, BigDecimal::add);
                    e.setOriginalMoney(e.getMoney());
                    e.setMoney(e.getMoney().add(strikeBalance));
                    // 如果消费费用是商品
                    if (FeeSubjectType.SHANG_PIN.getKey().equals(e.getSubjectType()) || FeeSubjectType.DIAN_PIN.getKey().equals(e.getSubjectType())){
                        Optional<BusOrderFeeGoodsVo> busOrderFeeGoodsVo = busOrderFeeGoodVos.stream()
                                .filter(orderFeeGoodVo -> StrUtil.equals(orderFeeGoodVo.getOrderFeeId(), e.getId())).findFirst();
                        busOrderFeeGoodsVo.ifPresent(e::setFeeGoodVo);
                    } else if (FeeSubjectType.SHUI_DIAN_MEI.getKey().equals(e.getSubjectType())){
                        Optional<CesHouseLongRentMeterLog> houseLongRentMeterLogs = longRentMeterLogs.stream()
                                .filter(meterLog -> StrUtil.equals(meterLog.getOrderFeeId(), e.getId())).findFirst();
                        houseLongRentMeterLogs.ifPresent(e::setLongRentMeterLog);
                    }
                    orderFeeList.add(e);
                }

            });
            return orderFeeList;
        }
        return new ArrayList<>();
    }

    public Boolean syncDayRoomFee(String hotelId){
        List<CesRooms> hotelRooms = roomsService.list(Wrappers.<CesRooms>query().eq("hotel_id",hotelId).eq("invalid",false));
        List<CesRooms> inLiveRooms = hotelRooms.stream().filter(s->s.getLivingOrderId() != null && !s.getLivingOrderId().isEmpty()
                && (s.getRoomStatus().equals(RoomStatusEnum.LIVE_DIRTY.getKey()) || s.getRoomStatus().equals(RoomStatusEnum.LIVE_CLEAR.getKey()))).collect(Collectors.toList());
        List<String> livingOrderIds = inLiveRooms.stream().map(CesRooms::getLivingOrderId).collect(Collectors.toList());
        List<BusRoomsLivingOrder> livingOrders = roomsLivingOrderService.list(Wrappers.<BusRoomsLivingOrder>query()
                .in("id",livingOrderIds));
        List<CesAllDayPriceRule> allDayPriceRules = allDayPriceRuleService.list();
        Optional<CesAllDayPriceRule> opAllPrice = allDayPriceRules.stream().filter(a->a.getHotelId().equals(hotelId)).findFirst();
//        CesAllDayPriceRule allPrice;
//        if(!opAllPrice.isPresent()) {
//            allPrice = new CesAllDayPriceRule();
//            allPrice.setEndTime("16:00");
//        } else {
//            allPrice = opAllPrice.get();
//        }
        livingOrders.forEach(s->{
            try {
                Date dueDate = DateUtils.parseDate(DateUtils.formatDate(s.getDueOutTime()),"yyyy-MM-dd");
                Date currDate = DateUtils.parseDate(DateUtils.formatDate(new Date()),"yyyy-MM-dd");
                List<BusOrderFee> hasFeeList = feeService.list(Wrappers.<BusOrderFee>lambdaQuery().eq(BusOrderFee::getFeeType, 1)
                        .eq(BusOrderFee::getSubjectType, FeeSubjectType.MEI_RI_FANG_FEI.getKey()).ge(BusOrderFee::getCreateTime, currDate));
                if (!hasFeeList.isEmpty()){
                    return;
                }
                List<BusLivingLayoutDayPrice> dayPrices = livingLayoutDayPriceService.list(Wrappers.<BusLivingLayoutDayPrice>query()
                        .eq("living_order_id",s.getId()));
                Optional<BusLivingLayoutDayPrice> opPrice = dayPrices.stream().filter(a-> a.getDayTime().compareTo(currDate) == 0).findFirst();
                BigDecimal money = new BigDecimal(0);
                if(!opPrice.isPresent()){
                    BusBookingRooms br = bookingRoomsService.getOne(Wrappers.<BusBookingRooms>query()
                            .eq("id",s.getBookingRoomId()));
                    if(br == null) return;
                    CesRooms room =  roomsService.getById(br.getRoomId());
                    if(room == null) return;
                    CesRoomLayout layout = layoutService.getById(room.getLayoutId());
                    if(layout == null) return;
                    BusLivingLayoutDayPrice nPrice = new BusLivingLayoutDayPrice();
                    nPrice.setBookingRoomId(s.getBookingRoomId());
                    try {
                        nPrice.setDayTime(DateUtils.parseDate(DateUtils.formatDate(new Date()),"yyyy-MM-dd"));
                    } catch (ParseException e) {
                        e.printStackTrace();
                    }
                    nPrice.setRoomId(br.getRoomId());
                    nPrice.setRoomLayoutId(room.getLayoutId());
                    nPrice.setPrice(layout.getMarketPrice());
                    nPrice.setLivingOrderId(s.getId());
                    nPrice.setLivingType(s.getLivingType());
                    livingLayoutDayPriceService.save(nPrice);
                    // 更新订单日期
                    if(dueDate.getTime() < currDate.getTime()){
                        Date date = s.getDueOutTime();
                        Calendar calendar = new GregorianCalendar();
                        calendar.setTime(date);
                        calendar.add(Calendar.DATE,1);
                        s.setDueOutTime(calendar.getTime());
                        roomsLivingOrderService.updateById(s);
                    }
                    money = layout.getMarketPrice();
                } else {
                    money = opPrice.get().getPrice();
                }

                BusOrderFee dayOrderFee = new BusOrderFee();
                dayOrderFee.setDayTime(new Date());
                dayOrderFee.setCreateTime(new Date());

                dayOrderFee.setFeeType(1);
                dayOrderFee.setSubjectType(FeeSubjectType.MEI_RI_FANG_FEI.getKey());

                CesRooms orderRoom = inLiveRooms.stream().filter(a->a.getLivingOrderId().equals(s.getId())).findFirst().get();
                dayOrderFee.setMoney(money);
                dayOrderFee.setRoomId(orderRoom.getId());
                String roomName = orderRoom.getPrefix() != null?orderRoom.getPrefix():"";
                roomName += roomName + orderRoom.getName();
                dayOrderFee.setRemark(DateUtils.formatDate(new Date(), "MM-dd") + "房费");
                dayOrderFee.setLivingOrderId(s.getId());
                dayOrderFee.setHotelId(hotelId);
                feeService.save(dayOrderFee);
            } catch (ParseException e) {
                e.printStackTrace();
            }
        });
        return null;
    }


    public Boolean syncRoomOverTimeOrder(String hotelId) {
        // 查询出当前时间内需要处理的超时订单
        Set<Object> set = redisTemplate.opsForZSet().rangeByScore("overTimerOrder", 0, Calendar.getInstance().getTimeInMillis());
        CesAllDayPriceRule cesAllDayPriceRule =  allDayPriceRuleService.getOne(Wrappers.<CesAllDayPriceRule>lambdaQuery().eq(CesAllDayPriceRule::getHotelId,hotelId).eq(CesAllDayPriceRule::getInvalid,false));
        if(CollUtil.isEmpty(set)){
            return true;
        }
        if(ObjectUtils.isEmpty(cesAllDayPriceRule)) {
            throw new JeecgBootException("未找到超时收费规则");
        }
        LambdaQueryWrapper<BusRoomsLivingOrder> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.in(BusRoomsLivingOrder::getId,set);
        queryWrapper.eq(BusRoomsLivingOrder::getHotelId,hotelId);
        List<BusRoomsLivingOrder> livingOrderList = roomsLivingOrderService.list(queryWrapper);
        List<String> bookingIds = livingOrderList.stream().map(BusRoomsLivingOrder::getBookingOrderId).collect(Collectors.toList());
        if (bookingIds.isEmpty()){
            return true;
        }
        List<CesHourRoomRule> cesHourRoomRuleList = hourRoomRuleService.list(Wrappers.<CesHourRoomRule>lambdaQuery().eq(CesHourRoomRule::getHotelId, hotelId));
        List<BusRoomBookingOrders> bookingOrders = list(Wrappers.<BusRoomBookingOrders>lambdaQuery().in(BusRoomBookingOrders::getId, bookingIds));
        livingOrderList.forEach(e -> {
            Optional<BusRoomBookingOrders> optional = bookingOrders.stream().filter(ele -> ele.getId().equals(e.getBookingOrderId())).findFirst();
            if (!optional.isPresent()){
                return;
            }
//            // 全天房
//            if (ObjectUtil.equals(1,optional.get().getBookingType())){
//                // 如果房间超时是按分钟加收费用
//                if (TimeOutRuleEnum.ADD_BY_MINUTE.getKey().equals(cesAllDayPriceRule.getTimeOutRule())){
//                    addByMinute(e, cesAllDayPriceRule);
//                }else if (TimeOutRuleEnum.ADD_BY_DAY.getKey().equals(cesAllDayPriceRule.getTimeOutRule())){
//                    addByDay(e, cesAllDayPriceRule);
//                }else if (TimeOutRuleEnum.NOT_ADD.getKey().equals(cesAllDayPriceRule.getTimeOutRule())) {
//                    notAdd(e, cesAllDayPriceRule);
//                }
//                // 钟点房
//            } else if(ObjectUtil.equals(2,optional.get().getBookingType())){
//                addHourFee(cesHourRoomRuleList, e, cesAllDayPriceRule);
//            } else if(ObjectUtil.equals(5,optional.get().getBookingType())) {
//                addLongRentFee(e);
//            }
            // 全天房
            String strategyStr = "";
            if (ObjectUtil.equals(1,optional.get().getBookingType())){
                // 如果房间超时是按分钟加收费用
                if (TimeOutRuleEnum.ADD_BY_MINUTE.getKey().equals(cesAllDayPriceRule.getTimeOutRule())){
                    strategyStr = "minuteAddStrategy";
                }else if (TimeOutRuleEnum.ADD_BY_DAY.getKey().equals(cesAllDayPriceRule.getTimeOutRule())){
                    strategyStr = "dayAddStrategy";
                }else if (TimeOutRuleEnum.NOT_ADD.getKey().equals(cesAllDayPriceRule.getTimeOutRule())) {
                    strategyStr = "notAddStrategy";
                }
                // 钟点房
            } else if(ObjectUtil.equals(2,optional.get().getBookingType())){
                strategyStr = "hourRoomStrategy";
            } else if(ObjectUtil.equals(5,optional.get().getBookingType())) {
                strategyStr = "longRentStrategy";
            }
            try {
                pricingStrategyFactory.getStragegy(strategyStr).calculateFee(e, cesAllDayPriceRule, cesHourRoomRuleList);
            } catch (Exception ex) {
                return;
            }
        });
        return null;
    }

    public void addLongRentFee(BusRoomsLivingOrder livingOrder) {
        BusBookingRooms bookingroom = bookingRoomsService.getById(livingOrder.getBookingRoomId());
        if (ObjectUtil.isEmpty(bookingroom)){
            log.info("未找到订单{}对应的房间", livingOrder.getId());
            return;
        }
        CesRooms room = roomsService.getById(bookingroom.getRoomId());
        List<CesHouseLongRentCharge> list = houseLongRentChargeService.list(Wrappers.<CesHouseLongRentCharge>
                lambdaQuery().eq(CesHouseLongRentCharge::getLivingOrderId, livingOrder.getId())
                .eq(CesHouseLongRentCharge::getChargeType, ChargeTypeEnum.PERIODICITY.getKey()));
        if (list.isEmpty()){
            return;
        }
        Integer cycle = list.get(0).getNum();
        List<BusOrderFee> orderFees = new ArrayList<>();
        Calendar nowCalendar = Calendar.getInstance();
        Calendar arriveCalendar = Calendar.getInstance();
        arriveCalendar.setTime(livingOrder.getArrivalTime());
        int year = nowCalendar.get(Calendar.YEAR) - arriveCalendar.get(Calendar.YEAR);
        int month = nowCalendar.get(Calendar.MONTH) - arriveCalendar.get(Calendar.MONTH);
        // 当前与入住时相距月份
        int monthDiffer = year * 12 + month;
        Calendar dueOutCalendar = Calendar.getInstance();
        dueOutCalendar.setTime(livingOrder.getDueOutTime());
        int allYear = dueOutCalendar.get(Calendar.YEAR) - arriveCalendar.get(Calendar.YEAR);
        int allMonth = dueOutCalendar.get(Calendar.MONTH) - arriveCalendar.get(Calendar.MONTH);
        // 总入住月份
        int allMonthDiffer = allYear * 12 + allMonth;
        if (monthDiffer >= allMonthDiffer){
            return;
        }
        // 下一次收费月份
        arriveCalendar.set(Calendar.YEAR, nowCalendar.get(Calendar.YEAR));
        arriveCalendar.set(Calendar.MONTH, nowCalendar.get(Calendar.MONTH));
        Integer nextMonthNum = allMonthDiffer - monthDiffer < cycle ? allMonthDiffer - monthDiffer : cycle;
        arriveCalendar.add(Calendar.MONTH, nextMonthNum);
        list.forEach(e -> {
            BusOrderFee dayOrderFee = new BusOrderFee();
            dayOrderFee.setDayTime(new Date());
            dayOrderFee.setCreateTime(new Date());
            dayOrderFee.setRemark(e.getName());
            String roomName = room.getPrefix() != null? room.getPrefix():"";
            roomName += roomName + room.getName();
            dayOrderFee.setMoney(e.getMoney());
            dayOrderFee.setSubjectType(FeeSubjectType.OTHER.getKey());
            dayOrderFee.setMoney(e.getMoney().multiply(BigDecimal.valueOf(nextMonthNum)));
            if (e.getIsMust() != null && e.getIsMust()){
                dayOrderFee.setSubjectType(FeeSubjectType.MEI_RI_FANG_FEI.getKey());
                dayOrderFee.setRemark(DateUtil.format(new Date(), "yyyy-MM") + e.getName());
            }
            dayOrderFee.setFeeType(1);
            dayOrderFee.setRoomId(room.getId());
            dayOrderFee.setLivingOrderId(livingOrder.getId());
            dayOrderFee.setHotelId(room.getHotelId());
            orderFees.add(dayOrderFee);
        });
        feeService.saveBatch(orderFees);
        redisTemplate.opsForZSet().remove("overTimerOrder",livingOrder.getId());
        redisTemplate.opsForZSet().add("overTimerOrder",livingOrder.getId(), arriveCalendar.getTimeInMillis());
    }


    private void addHourFee(List<CesHourRoomRule> cesHourRoomRuleList, BusRoomsLivingOrder livingOrder, CesAllDayPriceRule cesAllDayPriceRule) {
        // TODO: 2023/10/24 钟点房超时收费
        Optional<CesHourRoomRule> ruleOptional = cesHourRoomRuleList.stream().filter(rule -> rule.getId().equals(livingOrder.getHourRoomId())).findFirst();
        ruleOptional.ifPresent(rule -> {
            Calendar nowCalendar = Calendar.getInstance();
            Calendar nextCalendar = Calendar.getInstance();
            nextCalendar.setTime(livingOrder.getDueOutTime());
            Integer overMinutes = Math.round((nowCalendar.getTimeInMillis() - nextCalendar.getTimeInMillis()) / (1000 * 60 ));
            if (overMinutes <= 0){
                return;
            }
            BusBookingRooms bookingroom = bookingRoomsService.getById(livingOrder.getBookingRoomId());
            if (ObjectUtil.isEmpty(bookingroom)){
                log.info("未找到订单{}对应的房间", livingOrder.getId());
                return;
            }
            BusOrderFee fee = getOverTimeFee(livingOrder, bookingroom);
            // 转为全天房
            if (rule.getLimitTimeTransferAllDay() != 0 && overMinutes > rule.getLimitTimeTransferAllDay()){
                Date arrivalTime = livingOrder.getArrivalTime();
                Calendar arrivalCalendar = Calendar.getInstance();
                arrivalCalendar.setTime(arrivalTime);
                DateTime leaveTime = DateUtil.parse(cesAllDayPriceRule.getLeaveTime(), "HH:mm");
                Calendar leaveCalendar = Calendar.getInstance();
                leaveCalendar.setTime(leaveTime);
                leaveCalendar.set(Calendar.YEAR, arrivalCalendar.get(Calendar.YEAR));
                leaveCalendar.set(Calendar.MONTH, arrivalCalendar.get(Calendar.MONTH));
                leaveCalendar.set(Calendar.DAY_OF_MONTH, arrivalCalendar.get(Calendar.DAY_OF_MONTH));
                Calendar enterCalendar = Calendar.getInstance();
                DateTime enterTime = DateUtil.parse(cesAllDayPriceRule.getEnterTime(), "HH:mm");
                enterCalendar.setTime(enterTime);
                if (arrivalCalendar.get(Calendar.HOUR_OF_DAY) > enterCalendar.get(Calendar.HOUR_OF_DAY)
                        || arrivalCalendar.get(Calendar.HOUR_OF_DAY) == enterCalendar.get(Calendar.HOUR_OF_DAY)
                        && arrivalCalendar.get(Calendar.MINUTE) > enterCalendar.get(Calendar.MINUTE)){
                    // 如果预抵时间在enterTime后面 则预离时间为隔天
                    leaveCalendar.add(Calendar.DAY_OF_YEAR, 1);
                }
                livingOrder.setDueOutTime(leaveCalendar.getTime());
                livingOrder.setLivingType(LivingTypeEnum.ALL_DAY.getKey());
                List<BusOrderFee> busOrderFees = feeService.list(Wrappers.<BusOrderFee>lambdaQuery()
                        .eq(BusOrderFee::getLivingOrderId, livingOrder.getId()).eq(BusOrderFee::getSubjectType, FeeSubjectType.MEI_RI_FANG_FEI.getKey()));
                Optional<BusOrderFee> orderFee = busOrderFees.stream().filter(e -> !e.getIsOverTime()).findFirst();

                // 修改房费为全天房费
                orderFee.ifPresent(e -> {
                    BusLivingLayoutDayPrice dayPrice = livingLayoutDayPriceService.getOne(Wrappers.<BusLivingLayoutDayPrice>lambdaQuery()
                            .eq(BusLivingLayoutDayPrice::getLivingOrderId, livingOrder.getId())
                            .eq(BusLivingLayoutDayPrice::getDayTime, DateUtils.formatDate(livingOrder.getArrivalTime())).last("limit 1"));
                    BigDecimal money = schemeLayoutDailyPriceService.getOneDayPrice(bookingroom.getRoomLayoutId(), livingOrder, new Date());
                    e.setMoney(money);
                    feeService.updateById(e);
                    if (dayPrice != null){
                        dayPrice.setPrice(money);
                        livingLayoutDayPriceService.updateById(dayPrice);
                    }
                });
                // 删除超时收费项
                Optional<BusOrderFee> overTimeFee = busOrderFees.stream().filter(BusOrderFee::getIsOverTime).findFirst();
                overTimeFee.ifPresent(e -> {
                    feeService.removeById(e.getId());
                });
                roomsLivingOrderService.updateById(livingOrder);
                leaveCalendar.add(Calendar.MINUTE, Integer.parseInt(cesAllDayPriceRule.getDayTime()));
                redisTemplate.opsForZSet().remove("overTimerOrder",livingOrder.getId());
                redisTemplate.opsForZSet().add("overTimerOrder",livingOrder.getId(),leaveCalendar.getTimeInMillis());
                return;
            }
            //已经超时的 不足时间
            Integer minutesDiff = overMinutes % rule.getExceedTime();
            // 超出 超时时间 的数量
            Integer minutesDiffNum = (int) Math.floor(overMinutes / rule.getExceedTime());
            // 不足时间 数量
            Integer moreThenMinuteDiffNum = (int) Math.floor(minutesDiff / rule.getNotReachExceedTime());
            // 加上下一次不足时间 >= 超出时间，用超出时间
            if (minutesDiff + rule.getNotReachExceedTime() > rule.getExceedTime()){
                Integer newOverTime = rule.getExceedTime() * (minutesDiffNum + 1);
                nextCalendar.add(Calendar.MINUTE, newOverTime);
            }else {
                nextCalendar.add(Calendar.MINUTE, rule.getNotReachExceedTime());
            }
            log.info("钟点房{}今日超时分钟{}，超时时间数量{}，不足时间数量{}",livingOrder.getId(),overMinutes,minutesDiffNum,moreThenMinuteDiffNum);
            BigDecimal money = rule.getExceedPay().multiply(BigDecimal.valueOf(minutesDiffNum))
                    .add(rule.getNotReachExceedTimePay().multiply(BigDecimal.valueOf(moreThenMinuteDiffNum)));
            // 有上限，且金额超过当前上限
            if (rule.getMaxExtraPay().compareTo(new BigDecimal(0)) != 0 && rule.getMaxExtraPay().compareTo(money) < 0){
                money = rule.getMaxExtraPay();
            }
            fee.setMoney(money);
            fee.setDayTime(nowCalendar.getTime());
            redisTemplate.opsForZSet().remove("overTimerOrder",livingOrder.getId());

            SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm");
            log.info(format.format(nextCalendar.getTime()));
            redisTemplate.opsForZSet().add("overTimerOrder",livingOrder.getId(),nextCalendar.getTimeInMillis());
            // 添加超时房费
            feeService.saveOrUpdate(fee);
        });
    }

    /**
     * 超时收费规则是按分钟
     */
    public void addByMinute(BusRoomsLivingOrder livingOrder, CesAllDayPriceRule cesAllDayPriceRule){
        Date date = new Date();
        Calendar nextCalendar = Calendar.getInstance();
        Calendar nowCalendar = Calendar.getInstance();
        nextCalendar.setTime(date);
        BusBookingRooms bookingroom = bookingRoomsService.getById(livingOrder.getBookingRoomId());
        if (ObjectUtil.isEmpty(bookingroom)){
            log.info("未找到订单{}对应的房间", livingOrder.getId());
            return;
        }
        // 查询当天房间超时消费
        BusOrderFee fee = getOverTimeFee(livingOrder, bookingroom);
        // 需要支付完整费用时间的最后时间
        Date endTime = getFullFeeTime(date, cesAllDayPriceRule);
        // 当前时间加上超时收费时间
//        nextCalendar.add(Calendar.MINUTE, cesAllDayPriceRule.getMinute());
        // 获取今日第一次超时时间
        nextCalendar = getRemindTime(livingOrder, cesAllDayPriceRule, 1);
        nextCalendar.set(Calendar.YEAR, nowCalendar.get(Calendar.YEAR));
        nextCalendar.set(Calendar.MONTH, nowCalendar.get(Calendar.MONTH));
        nextCalendar.set(Calendar.DATE, nowCalendar.get(Calendar.DATE));
        // 今日超时分钟
        Integer overMinutes = Math.round((nowCalendar.getTimeInMillis() - nextCalendar.getTimeInMillis()) / (1000 * 60 ));
        if (overMinutes < 0){
            return;
        }
        //已经超时的 不足时间
        Integer minutesDiff = overMinutes % cesAllDayPriceRule.getMinute();
        // 超出 超时时间 的数量
        Integer minutesDiffNum = (int) Math.floor(overMinutes / cesAllDayPriceRule.getMinute());

        Integer moreThenMinuteDiffNum = (int) Math.floor(minutesDiff / cesAllDayPriceRule.getMoreThenMinute());
        // 加上下一次不足时间 >= 超出时间，用超出时间
        if (minutesDiff + cesAllDayPriceRule.getMoreThenMinute() > cesAllDayPriceRule.getMinute()){
            Integer newOverTime = cesAllDayPriceRule.getMinute() * (minutesDiffNum + 1);
            nextCalendar.add(Calendar.MINUTE, newOverTime);
        }else {
            nextCalendar.add(Calendar.MINUTE, cesAllDayPriceRule.getMoreThenMinute());
        }
//        log.info(DateUtil.format(nextCalendar.getTime(), "yyyy-MM-dd HH:mm"));

        log.info("{}今日超时分钟{}，超时时间数量{}，不足时间数量{}",livingOrder.getId(),overMinutes,minutesDiffNum,moreThenMinuteDiffNum);

        // 当前时间加上超时收费时间 < 按全天收费时间的标准
        if (nextCalendar.getTime().compareTo(endTime) < 0){
            // 不满足的情况下，添加超时收费，设置下一次收费时间戳
            BigDecimal money = cesAllDayPriceRule.getPrice().multiply(BigDecimal.valueOf(minutesDiffNum))
                    .add(cesAllDayPriceRule.getMoreThenPrice().multiply(BigDecimal.valueOf(moreThenMinuteDiffNum)));
            fee.setDayTime(date);
            fee.setMoney(money);
            redisTemplate.opsForZSet().remove("overTimerOrder",livingOrder.getId());

            // 如果下一次增加收费时间大于 全天收费时间 设置
        }else {
            // 如果满足全天收费要求，预离时间增加1天
            BusLivingLayoutDayPrice dayPrice = addDayPrice(livingOrder, bookingroom, bookingroom.getRoomLayoutId());
            fee.setMoney(dayPrice.getPrice());
            fee.setDayTime(date);
            // 获取每天超时时间，如果超过预离时间，修改预离时间
            nextCalendar = getNextCalendar(livingOrder, nowCalendar,cesAllDayPriceRule);
            fee.setRemark(DateUtils.formatDate(nowCalendar.getTime(), "MM-dd") + "房费");
            fee.setIsOverTime(false);
        }
        // 移除处理过的时间订单，添加订单下一次超时的时间戳
        redisTemplate.opsForZSet().remove("overTimerOrder",livingOrder.getId());
        redisTemplate.opsForZSet().add("overTimerOrder",livingOrder.getId(),nextCalendar.getTimeInMillis());
        // 添加超时房费
        feeService.saveOrUpdate(fee);
    }


    public void addByDay(BusRoomsLivingOrder livingOrder, CesAllDayPriceRule cesAllDayPriceRule){
        Date date = new Date();
        Calendar nowCalendar = Calendar.getInstance();
        Calendar nextCalendar = Calendar.getInstance();
        BusBookingRooms bookingroom = bookingRoomsService.getById(livingOrder.getBookingRoomId());
        if (ObjectUtil.isEmpty(bookingroom)) {
            log.info("未找到订单{}对应的房间", livingOrder.getId());
            return;
        }
        // 查询当天房间超时消费
        BusOrderFee fee = getOverTimeFee(livingOrder, bookingroom);
        // 需要支付完整费用时间的最后时间
        Date endTime = getFullFeeTime(date, cesAllDayPriceRule);
        BusLivingLayoutDayPrice dayPrice = addDayPrice(livingOrder, bookingroom, bookingroom.getRoomLayoutId());
        if (nextCalendar.getTime().compareTo(endTime) < 0){
            // 超时收取半天费用
            fee.setMoney(dayPrice.getPrice().divide(new BigDecimal(2)));
            nextCalendar.setTime(endTime);
        } else {
            // 添加每日房费
            fee.setMoney(dayPrice.getPrice());
            fee.setIsOverTime(false);
            // 获取每天超时时间，如果超过预离时间，修改预离时间
            nextCalendar = getNextCalendar(livingOrder, nowCalendar,cesAllDayPriceRule);
        }
        // 移除处理过的时间订单，添加订单下一次超时的时间戳
        redisTemplate.opsForZSet().remove("overTimerOrder",livingOrder.getId());
        redisTemplate.opsForZSet().add("overTimerOrder",livingOrder.getId(),nextCalendar.getTimeInMillis());
        feeService.saveOrUpdate(fee);
    }

    public void notAdd(BusRoomsLivingOrder livingOrder, CesAllDayPriceRule cesAllDayPriceRule){
        Date date = new Date();
        Calendar nowCalendar = Calendar.getInstance();
        Calendar nextCalendar = Calendar.getInstance();
        BusBookingRooms bookingroom = bookingRoomsService.getById(livingOrder.getBookingRoomId());
        if (ObjectUtil.isEmpty(bookingroom)) {
            log.info("未找到订单{}对应的房间", livingOrder.getId());
            return;
        }
        // 查询当天房间超时消费
        BusOrderFee fee = getOverTimeFee(livingOrder, bookingroom);
        // 需要支付完整费用时间的最后时间
        Date endTime = getFullFeeTime(date, cesAllDayPriceRule);
        if (nextCalendar.getTime().compareTo(endTime) >= 0){
            // 添加每日房费
            BusLivingLayoutDayPrice dayPrice = addDayPrice(livingOrder, bookingroom, bookingroom.getRoomLayoutId());
            fee.setMoney(dayPrice.getPrice());
            fee.setIsOverTime(false);
            // 获取每天超时时间，如果超过预离时间，修改预离时间
            nextCalendar = getNextCalendar(livingOrder, nowCalendar,cesAllDayPriceRule);
        }
        // 移除处理过的时间订单，添加订单下一次超时的时间戳
        redisTemplate.opsForZSet().remove("overTimerOrder",livingOrder.getId());
        redisTemplate.opsForZSet().add("overTimerOrder",livingOrder.getId(),nextCalendar.getTimeInMillis());
        feeService.saveOrUpdate(fee);
    }

    public BusLivingLayoutDayPrice addDayPrice(BusRoomsLivingOrder livingOrder, BusBookingRooms bookingroom, String layoutId){
        List<BusLivingLayoutDayPrice> dayPrices = livingLayoutDayPriceService.list(Wrappers.<BusLivingLayoutDayPrice>query()
                .eq("living_order_id",livingOrder.getId()));
        Optional<BusLivingLayoutDayPrice> opPrice = dayPrices.stream().filter(a-> DateUtils.formatDate(a.getDayTime()).equals(DateUtils.formatDate(livingOrder.getDueOutTime()))).findFirst();
        if(!opPrice.isPresent()){
            // 根据房价方案查询价格
            BigDecimal money = schemeLayoutDailyPriceService.getOneDayPrice(layoutId, livingOrder, new Date());
            BusLivingLayoutDayPrice nPrice = new BusLivingLayoutDayPrice();
            nPrice.setBookingRoomId(livingOrder.getBookingRoomId());
            try {
                nPrice.setDayTime(DateUtils.parseDate(DateUtils.formatDate(new Date()),"yyyy-MM-dd"));
            } catch (ParseException e) {
                e.printStackTrace();
            }

            nPrice.setRoomId(bookingroom.getRoomId());
            nPrice.setRoomLayoutId(bookingroom.getRoomLayoutId());
            nPrice.setPrice(money);
            nPrice.setLivingOrderId(livingOrder.getId());
            nPrice.setLivingType(livingOrder.getLivingType());
            livingLayoutDayPriceService.save(nPrice);
            return nPrice;
        }
        return opPrice.get();
    }

    public BusOrderFee getOverTimeFee(BusRoomsLivingOrder livingOrder, BusBookingRooms bookingroom){
//        Calendar nowCalendar = Calendar.getInstance();
//        nowCalendar.set(Calendar.HOUR_OF_DAY, 0);
//        nowCalendar.set(Calendar.MINUTE, 0);
//        nowCalendar.set(Calendar.SECOND, 0);
//        nowCalendar.set(Calendar.MILLISECOND, 0);
        List<BusOrderFee> feeList = feeService.list(Wrappers.<BusOrderFee>lambdaQuery()
                .eq(BusOrderFee::getLivingOrderId, livingOrder.getId()).eq(BusOrderFee::getPreferentialStatus, 1)
                .eq(BusOrderFee::getSubjectType, FeeSubjectType.MEI_RI_FANG_FEI.getKey()).eq(BusOrderFee::getIsOverTime, true)
                .orderByDesc(BusOrderFee::getCreateTime));
        if (CollUtil.isNotEmpty(feeList)) {
            return feeList.get(0);
        }
        BusOrderFee fee = new BusOrderFee();
        fee.setMoney(new BigDecimal(0));
        fee.setCreateTime(new Date());
        fee.setDayTime(new Date());
        fee.setSubjectType(FeeSubjectType.MEI_RI_FANG_FEI.getKey());
        fee.setLivingOrderId(livingOrder.getId());
        fee.setHotelId(livingOrder.getHotelId());
        fee.setFeeType(FeeType.CONSUME.getKey());
        fee.setRoomId(bookingroom.getRoomId());
        fee.setRemark("超时房费");
        fee.setIsOverTime(true);
        return fee;
    }

    private Calendar getNextCalendar(BusRoomsLivingOrder livingOrder, Calendar nowCalendar,CesAllDayPriceRule cesAllDayPriceRule) {
        Calendar nextCalendar = Calendar.getInstance();
        nextCalendar.setTime(livingOrder.getDueOutTime());
        nextCalendar.set(Calendar.YEAR, nowCalendar.get(Calendar.YEAR));
        nextCalendar.set(Calendar.MONTH, nowCalendar.get(Calendar.MONTH));
        nextCalendar.set(Calendar.DAY_OF_MONTH, nowCalendar.get(Calendar.DAY_OF_MONTH));
        nextCalendar.add(Calendar.DAY_OF_YEAR,1);
        // 修改预离时间
        if(nextCalendar.getTime().compareTo(livingOrder.getDueOutTime()) > 0){
            livingOrder.setDueOutTime(nextCalendar.getTime());
            roomsLivingOrderService.updateById(livingOrder);
        }
        // 下一次收费时间戳
        nextCalendar.add(Calendar.MINUTE, Integer.parseInt(cesAllDayPriceRule.getDayTime()));
        return nextCalendar;
    }

    /**
     * 完整费用时间
     * @return
     */
    public Date getFullFeeTime(Date date, CesAllDayPriceRule cesAllDayPriceRule){
        DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd ");
        String endDateStr = dateFormat.format(date);
        Date endTime = null;
        try {
            endTime = new SimpleDateFormat("yyyy-MM-dd HH:mm").parse(endDateStr + cesAllDayPriceRule.getEndTime());
        } catch (ParseException ex) {
            throw new JeecgBootException("结束时间格式异常");
        }
        return endTime;
    }


    @Override
    public BigDecimal getOrderFeeTotal(String livingOrderId, String bookingOrderId) {

        return null;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean continueLiving2(List<LivingContinueVo> continueLiving, String hotelId) {
        List<String> livingIds = continueLiving.stream().map(LivingContinueVo::getId).collect(Collectors.toList());
        if (CollUtil.isEmpty(livingIds)){
            return null;
        }
        List<BusRoomsLivingOrder> livingOrderList = roomsLivingOrderService.listByIds(livingIds);
        if (livingIds.size() != livingOrderList.size()){
            return null;
        }
        List<BusOrderFee> orderFeeList = new ArrayList<>();
        List<BusLivingLayoutDayPrice> dayPrices = new ArrayList<>();
        continueLiving.forEach(living ->{
            Optional<BusRoomsLivingOrder> orderOptional = livingOrderList.stream().filter(order -> order.getId().equals(living.getId())).findFirst();
            orderOptional.ifPresent(e -> {
                // 修改订单预离时间
                e.setDueOutTime(living.getDueOutTime());
                e.setBreakfastNum(living.getBreakfastNum());
                // 添加收款单
                if (ObjectUtil.isNotEmpty(living.getDayNum()) && living.getDayNum() > 0
                        && ObjectUtil.isNotEmpty(living.getPayMoney()) && living.getPayMoney().compareTo(new BigDecimal(0)) > 0){
                    if (StrUtil.isEmpty(living.getPayType())){
                        throw new JeecgBootException("未找到付款方式");
                    }
                    BusOrderFee orderFee = new BusOrderFee();
                    orderFee.setMoney(living.getPayMoney());
                    orderFee.setPayType(living.getPayType());
                    orderFee.setVipCardId(living.getVipCustomerId());
                    orderFee.setAgreementUnitId(living.getContractTeamId());
                    orderFee.setFeeType(2);
                    orderFee.setSubjectType(2);
                    orderFee.setRemark("预收房费");
                    orderFee.setLivingOrderId(e.getId());
                    orderFee.setPreferentialStatus(1);
                    orderFee.setRoomId(living.getRoomId());
                    orderFee.setHotelId(hotelId);
                    orderFeeList.add(orderFee);
                }
                // 
                if (living.getDayNum() < 0){
                    // TODO: 2023/10/19 删除预约每日房价，还不确定要不要删
                } else {
                    dayPrices.addAll(living.getLayoutDayPriceList());
                    living.setIsStayOver(true);
                }
                
            });
        });
        feeService.collectionBatch(orderFeeList, hotelId);
        roomsLivingOrderService.updateBatchById(livingOrderList);
        livingLayoutDayPriceService.saveBatch(dayPrices);
        return true;
//        throw new JeecgBootException("设置失败");
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean continueLiving(BusOrderFee settleFee, Date dueOutTime,String livingOrderId,String hotelId) {
        BusRoomsLivingOrder order = roomsLivingOrderService.getById(livingOrderId);
        if (order == null) throw new JeecgBootException("订单错误");
        BusBookingRooms bkRoom = bookingRoomsService.getById(order.getBookingRoomId());
        if (bkRoom == null) throw new JeecgBootException("订单房间不存在");
        if (CollUtil.isEmpty(settleFee.getPrices()) && (settleFee.getMoney() == null || settleFee.getMoney().equals(new BigDecimal(0)))){
            return true;
        }
        if (order.getArrivalTime().getTime() >= dueOutTime.getTime()) throw new JeecgBootException("预离时间不能小于预抵时间");
        if (settleFee != null && settleFee.getMoney() != null && !settleFee.getMoney().equals(new BigDecimal(0))) {
            settleFee.setLivingOrderId(livingOrderId);
            settleFee.setFeeType(2);
            settleFee.setSubjectType(FeeSubjectType.YU_SHOU.getKey());
            settleFee.setHotelId(hotelId);
            settleFee.setRoomId(bkRoom.getRoomId());
            settleFee.setPreferentialStatus(1); // 未结账
            feeService.save(settleFee);
        }
        if(dueOutTime.getTime() >= order.getDueOutTime().getTime()){
            CesRooms room =  roomsService.getById(bkRoom.getRoomId());
            settleFee.getPrices().forEach(s -> {
                s.setRoomLayoutId(room.getLayoutId());
                s.setRoomId(bkRoom.getRoomId());
                s.setLivingType(1);
                s.setLivingOrderId(livingOrderId);
            });
            livingLayoutDayPriceService.saveBatch(settleFee.getPrices());
        }
        order.setDueOutTime(dueOutTime);
        roomsLivingOrderService.updateById(order);
        return true;
//        throw new JeecgBootException("设置失败");
    }


    /**
     * 判断续租是否跟预约订单冲突
     */
    @Override
    public List<LivingContinueVo> roomIdBooked(List<LivingContinueVo> livingContinueList) {
        List<String> livingIds = livingContinueList.stream().map(LivingContinueVo::getId).collect(Collectors.toList());
        if (CollUtil.isEmpty(livingIds)) {
            return null;
        }
        List<BusRoomsLivingOrder> orders = roomsLivingOrderService.listByIds(livingIds);
        if (livingIds.size() != orders.size()) {
            return null;
        }
        List<LivingContinueVo> returnLivingContinueVos = new ArrayList<>();
        orders.forEach(e ->{
            Optional<LivingContinueVo> continueOptional = livingContinueList.stream().filter(ele -> e.getId().equals(ele.getId())).findFirst();
            List<LivingContinueVo> livingContinueVos =
                    baseMapper.findBookingOrderByRoom(e.getBookingRoomId(), new Date(), continueOptional.get().getDueOutTime());
            returnLivingContinueVos.addAll(livingContinueVos);
        });
        return returnLivingContinueVos;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean settleBillCheckOut(List<BusOrderFee> settleFees, String bookingOrderId) {
        if(bookingOrderId != null && !bookingOrderId.isEmpty()) {
            List<BusRoomsLivingOrder> livingOrders = roomsLivingOrderService.list(Wrappers.<BusRoomsLivingOrder>query()
            .eq("booking_order_id",bookingOrderId));
            BusBookingRooms mainRoom = bookingRoomsService.getOne(Wrappers.<BusBookingRooms>query().eq("is_main", true)
            .eq("booking_orders_id",bookingOrderId));
            if(mainRoom == null) throw new JeecgBootException("没有找到主房");
            Optional<BusRoomsLivingOrder> opMainRoomOrder = livingOrders.stream().filter(s->s.getBookingRoomId().equals(mainRoom.getId())).findFirst();
            if(!opMainRoomOrder.isPresent()) throw new JeecgBootException("没有找到主房订单");
            BusRoomsLivingOrder mainRoomOrder = opMainRoomOrder.get();
            livingOrders.forEach(s->{
                // fix-bug 当结账后设置离店时间 2023-05-09
                s.setDueOutTime(new Date());
                s.setSettleType(SettleTypeEnum.SETTLE_LEAVE.getKey());
                s.setIsUnionSettled(s.getId().equals(mainRoomOrder.getId())?0:1);
            });
            roomsLivingOrderService.updateBatchById(livingOrders);
            if(livingOrders.size() > 0) {
               List<BusOrderFee> feeItems = getLivingOrderFee(bookingOrderId, null); // 找到未结账的

                final BigDecimal[] shoukuan = {new BigDecimal(0)};
                final BigDecimal[] xiaofei = {new BigDecimal(0)};

                feeItems.forEach(c->{
                    if(c.getFeeType().equals(1)) { // 收款
                        shoukuan[0] = shoukuan[0].add(c.getMoney());
                    } else if(c.getFeeType().equals(2)) { // 消费
                        xiaofei[0] = xiaofei[0].add(c.getMoney());
                    }
                });
                BigDecimal m = shoukuan[0].subtract(xiaofei[0]);
                BigDecimal nowMoney = settleFees.stream().map(BusOrderFee::getMoney).reduce(BigDecimal.ZERO,BigDecimal::add);
                if (new BigDecimal(0).equals(m.add(nowMoney))){
                    throw new JeecgBootException("账单未平账");
                }
//                if(m.doubleValue() < 0) settleFees.get(0).setCustorerOrderRemark("结账退款");
//                else settleFees.get(0).setCustorerOrderRemark("结账收款");
//               // 消费账单
//                List<BusOrderFee> xiaoFeiFees = feeItems.stream().filter(s->s.getFeeType().equals(1)).collect(Collectors.toList());
//                // 收款
//                List<BusOrderFee> shouKuanFees = feeItems.stream().filter(s->s.getFeeType().equals(2)).collect(Collectors.toList());
                settleFee(mainRoom, mainRoomOrder, feeItems, settleFees);
                List<CesRooms> baseRooms = roomsService.list(Wrappers.<CesRooms>query()
                .in("living_order_id",livingOrders.stream().map(s->s.getId()).collect(Collectors.toList()))
                );
                if(baseRooms.size() > 0) {
                    baseRooms.forEach(s->{
                        s.setRoomStatus(RoomStatusEnum.EMPTY_DIRTY.getKey());
                        s.setLastRoomStatus(RoomStatusEnum.EMPTY_DIRTY.getKey());
                        s.setLivingOrderId("");
                        s.setBookingOrderId("");
                    });
                    roomsService.updateBatchById(baseRooms);
                }
                List<BusTeam> teams = teamService.list(Wrappers.<BusTeam>query().eq("booking_order_id",bookingOrderId));
                if(CollUtil.isNotEmpty(teams)) {
                    teams.get(0).setIsDelete(1);
                    teamService.updateById(teams.get(0));
                }
                livingOrders.forEach(e -> {
                    redisTemplate.opsForZSet().remove("overTimerOrder",e.getId());
                });
                return true;
                // todo 对账
            } else throw new JeecgBootException("未找到任何入住订单");
        } throw new JeecgBootException("参数错误");

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean livingSettleBillCheckOut(List<BusOrderFee> settleFees, String livingOrderId) {
        if(livingOrderId == null || livingOrderId.isEmpty()) throw new JeecgBootException("参数错误");
        BusRoomsLivingOrder livingOrder = roomsLivingOrderService.getById(livingOrderId);
        if(livingOrder == null) throw new JeecgBootException("未找到任何入住订单");
        BusBookingRooms mainRoom = bookingRoomsService.getOne(Wrappers.<BusBookingRooms>query()
                .eq("id",livingOrder.getBookingRoomId()));
        if(mainRoom == null) throw new JeecgBootException("没有找到房间");
        if (!mainRoom.getIsMain()){
            String bookingId = splitLiving(livingOrderId);
            livingOrder = roomsLivingOrderService.getById(livingOrderId);
        }
        List<BusRoomsLivingOrder> releLivingingOrders = roomsLivingOrderService.list(Wrappers.<BusRoomsLivingOrder>lambdaQuery()
                .eq(BusRoomsLivingOrder::getBookingOrderId, livingOrder.getBookingOrderId()));
        if (CollUtil.isEmpty(releLivingingOrders)){
            throw new JeecgBootException("未找到预定订单");
        }
        releLivingingOrders = releLivingingOrders.stream().filter(e ->
                ObjectUtil.equal(SettleTypeEnum.SETTLE_LEAVE.getKey(),e.getSettleType())).collect(Collectors.toList());

        if(mainRoom.getIsMain().equals(true) && releLivingingOrders.size() > 1) throw new JeecgBootException("查询到其他关联房间，主房不能单独结账退房");

        BusRoomsLivingOrder mainRoomOrder = livingOrder;
        // fixbug 当结账后设置离店时间 2023-05-09
        livingOrder.setDueOutTime(new Date());
        livingOrder.setSettleType(SettleTypeEnum.SETTLE_LEAVE.getKey());
        roomsLivingOrderService.updateById(livingOrder);
        // 当前居住单中未结算的费用
        List<BusOrderFee> feeItems = getLivingOrderFees(livingOrderId);

        final BigDecimal[] shoukuan = {new BigDecimal(0)};
        final BigDecimal[] xiaofei = {new BigDecimal(0)};
        feeItems.forEach(c->{
            if(c.getFeeType().equals(1)) { // 收款
                shoukuan[0] = shoukuan[0].add(c.getMoney());
            } else if(c.getFeeType().equals(2)) { // 消费
                xiaofei[0] = xiaofei[0].add(c.getMoney());
            }
        });
        BigDecimal m = shoukuan[0].subtract(xiaofei[0]);
        BigDecimal nowMoney = settleFees.stream().map(BusOrderFee::getMoney).reduce(BigDecimal.ZERO,BigDecimal::add);
        if (new BigDecimal(0).equals(m.add(nowMoney))){
            throw new JeecgBootException("账单未平账");
        }
//        // 消费账单
//        List<BusOrderFee> xiaoFeiFees = feeItems.stream().filter(s->s.getFeeType().equals(1)).collect(Collectors.toList());
//        // 收款
//        List<BusOrderFee> shouKuanFees = feeItems.stream().filter(s->s.getFeeType().equals(2)).collect(Collectors.toList());
        settleFee(mainRoom, mainRoomOrder, feeItems, settleFees);
        List<CesRooms> baseRooms = roomsService.list(Wrappers.<CesRooms>query()
                .eq("living_order_id",livingOrderId)
        );
        if(baseRooms.size() > 0) {
            baseRooms.forEach(s->{
                s.setRoomStatus(RoomStatusEnum.EMPTY_DIRTY.getKey());
                s.setLastRoomStatus(RoomStatusEnum.EMPTY_DIRTY.getKey());
                s.setLivingOrderId("");
                s.setBookingOrderId("");
            });
            roomsService.updateBatchById(baseRooms);
        }
        redisTemplate.opsForZSet().remove("overTimerOrder",livingOrder.getId());
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean partialSettle(PartialSettleVo param){

        BusRoomsLivingOrder livingOrder = roomsLivingOrderService.getById(param.getLivingOrderId());
        if(livingOrder == null) throw new JeecgBootException("未找到任何入住订单");
        BusBookingRooms mainRoom = bookingRoomsService.getOne(Wrappers.<BusBookingRooms>query()
                .eq("id",livingOrder.getBookingRoomId()));
        if(mainRoom == null) throw new JeecgBootException("没有找到主房");

        BusRoomsLivingOrder mainRoomOrder = livingOrder;
        List<BusOrderFee> feeItems = getLivingOrderFees(param.getLivingOrderId()).stream().filter(
                s->s.getPreferentialStatus().equals(1) // 找到未结账的
                && param.getSelectedFeeIds().contains(s.getId()) // 找到要结账的项目
        ).collect(Collectors.toList());
        List<BusOrderFee> settleFees = param.getFees();
        settleFee(mainRoom, mainRoomOrder, feeItems, settleFees);
        return  true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String splitLiving(String livingOrderId) {
        BusRoomsLivingOrder livingOrder = roomsLivingOrderService.getById(livingOrderId);
        if(livingOrder == null) throw new JeecgBootException("订单不存在");
        BusBookingRooms bookingRoom = bookingRoomsService.getById(livingOrder.getBookingRoomId());
        if (bookingRoom == null){
            throw new JeecgBootException("房间订单不存在");
        }
        if (SettleTypeEnum.UN_SETTLE_LEAVE.getKey().equals(livingOrder.getSettleType())){
            throw new JeecgBootException("未结退房的房间不能拆房");
        }
        long count = roomsLivingOrderService.count(Wrappers.<BusRoomsLivingOrder>query().eq("booking_order_id", livingOrder.getBookingOrderId()));
        if (count == 2){
            UpdateWrapper<BusTeam> uw = new UpdateWrapper<>();
            uw.eq("booking_order_id", livingOrder.getBookingOrderId());
            uw.set("is_delete", 1);
            teamService.update(uw);
        }
        BusRoomBookingOrders newBookingOrder = copyBookingToAcceptOrder(livingOrder);
        newBookingOrder.setHotelId(livingOrder.getHotelId());
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS");
        newBookingOrder.setBookingOrdersNo("YD"+sdf.format(System.currentTimeMillis()));
        newBookingOrder.setBookingOrdersType(1);
        save(newBookingOrder);
        livingOrder.setBookingOrderId(newBookingOrder.getId());
        roomsLivingOrderService.updateById(livingOrder);
        LambdaUpdateWrapper<CesRooms> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.set(CesRooms::getBookingOrderId, "").eq(CesRooms::getId, bookingRoom.getRoomId());
        roomsService.update(updateWrapper);
        bookingRoom.setBookingOrdersId(newBookingOrder.getId());
        bookingRoom.setIsMain(true);
        bookingRoomsService.updateById(bookingRoom);

        return newBookingOrder.getId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean leaveNotSettle(String bookingOrderId, String livingOrderId){
        if (StrUtil.isEmpty(bookingOrderId) || StrUtil.isEmpty(livingOrderId)){
            throw new JeecgBootException("参数异常");
        }
        // 找出关联的所有居住单
        List<BusRoomsLivingOrder> livingOrderList = roomsLivingOrderService.list(Wrappers.<BusRoomsLivingOrder>lambdaQuery()
                .eq(BusRoomsLivingOrder::getBookingOrderId, bookingOrderId));
        long unLeaveCount = livingOrderList.stream().filter(e -> SettleTypeEnum.UN_LEAVE.getKey().equals(e.getSettleType())).count();
        // 需要先走不结的居住订单
        Optional<BusRoomsLivingOrder> orderOptional = livingOrderList.stream().filter(e -> e.getId().equals(livingOrderId)).findFirst();
        if (!orderOptional.isPresent()){
            throw new JeecgBootException("订单未找到");
        }
        BusBookingRooms bookingRooms = bookingRoomsService.getById(orderOptional.get().getBookingRoomId());
        if(bookingRooms == null) throw new JeecgBootException("数据异常");
        // 是主房，且有正常入住的附属房
        if(bookingRooms.getIsMain() && livingOrderList.size() > 1 && StrUtil.isEmpty(orderOptional.get().getContractTeamId())){
            if (unLeaveCount > 1){
                throw new JeecgBootException("为主房不能单独先走不结");
            }
            BusMarketAgreementUnit marketAgreementUnit = marketAgreementUnitService.getById(orderOptional.get().getContractTeamId());
            if (marketAgreementUnit == null){
                throw new JeecgBootException("为主房且顾客不是协议单位不能先走不结");
            }
        }
        CesRooms room = roomsService.getOne(Wrappers.<CesRooms>query().eq("id",bookingRooms.getRoomId()));
        if(room == null) throw new JeecgBootException("未找到订单信息");
        room.setLivingOrderId("");
        room.setRoomStatus(RoomStatusEnum.EMPTY_DIRTY.getKey());
        room.setLastRoomStatus(RoomStatusEnum.EMPTY_DIRTY.getKey());
        orderOptional.get().setSettleType(SettleTypeEnum.UN_SETTLE_LEAVE.getKey());
        roomsLivingOrderService.updateById(orderOptional.get());
        roomsService.updateById(room);
        return true;
    }

    @Override
    public Boolean cancelLeaveNotSettle(String bookingOrderId, String livingOrderId) {
        if(livingOrderId != null && !livingOrderId.isEmpty()) {
            BusRoomsLivingOrder livingOrder = roomsLivingOrderService.getById(livingOrderId);
            if(livingOrder == null) throw new JeecgBootException("订单未找到");
            BusBookingRooms bookingRooms = bookingRoomsService.getById(livingOrder.getBookingRoomId());
            if(bookingRooms == null) throw new JeecgBootException("数据异常");
            CesRooms room = roomsService.getOne(Wrappers.<CesRooms>query().eq("id",bookingRooms.getRoomId()));
            if(room == null) throw new JeecgBootException("未找到订单信息");
            if (!RoomStatusEnum.EMPTY_DIRTY.getKey().equals(room.getRoomStatus()) && !RoomStatusEnum.EMPTY_CLEAR.getKey().equals(room.getRoomStatus())){
                throw new JeecgBootException("房间不能重新入住");
            }
            room.setLivingOrderId(livingOrderId);
            room.setRoomStatus(RoomStatusEnum.LIVE_DIRTY.getKey());
            room.setLastRoomStatus(RoomStatusEnum.EMPTY_DIRTY.getKey());
            livingOrder.setSettleType(SettleTypeEnum.UN_LEAVE.getKey());
            roomsLivingOrderService.updateById(livingOrder);
            roomsService.updateById(room);
        } else throw new JeecgBootException("参数错误");
        return true;
    }

    @Override
    public Boolean cancelLeaveSettle(String bookingOrderId, String livingOrderId) {
        if (StrUtil.isEmpty(bookingOrderId)){
            throw new JeecgBootException("请选择订单");
        }
        List<BusRoomsLivingOrder> livingOrderList = roomsLivingOrderService.list(Wrappers.<BusRoomsLivingOrder>lambdaQuery()
                .eq(BusRoomsLivingOrder::getBookingOrderId, bookingOrderId));
        livingOrderList.forEach(livingOrder -> {
            BusBookingRooms bookingRooms = bookingRoomsService.getById(livingOrder.getBookingRoomId());
            if(bookingRooms == null) throw new JeecgBootException("数据异常");
            CesRooms room = roomsService.getOne(Wrappers.<CesRooms>query().eq("id",bookingRooms.getRoomId()));
            if(room == null) throw new JeecgBootException("未找到订单信息");
            if (!RoomStatusEnum.EMPTY_DIRTY.getKey().equals(room.getRoomStatus()) && !RoomStatusEnum.EMPTY_CLEAR.getKey().equals(room.getRoomStatus())){
                throw new JeecgBootException("房间不能重新入住");
            }
            room.setLivingOrderId(livingOrderId);
            room.setRoomStatus(RoomStatusEnum.LIVE_DIRTY.getKey());
            room.setLastRoomStatus(RoomStatusEnum.EMPTY_DIRTY.getKey());
            livingOrder.setSettleType(SettleTypeEnum.UN_LEAVE.getKey());
            roomsLivingOrderService.updateById(livingOrder);
            roomsService.updateById(room);
        });

        // 结账收款时产生的收款单（收款单可能为负）
        List<BusOrderFee> busOrderFees = feeService.list(Wrappers.<BusOrderFee>lambdaQuery().eq(BusOrderFee::getIsSettleFee, true)
                .eq(BusOrderFee::getLivingOrderId, livingOrderId));
        // 所有会员支付的收款单id
        List<String> vipFeeIds = busOrderFees.stream().filter(e -> StrUtil.isNotEmpty(e.getVipCardId()))
                .map(BusOrderFee::getId).collect(Collectors.toList());
        if (!vipFeeIds.isEmpty()){
            // 结账时会员支付日志
            List<BusMemberBalanceLog> balanceLogs = memberBalanceLogService.list(Wrappers
                    .<BusMemberBalanceLog>lambdaQuery().in(BusMemberBalanceLog::getOrderFeeId, vipFeeIds));
            Map<String, List<BusMemberBalanceLog>> balanceLogMap = balanceLogs.stream().collect(Collectors.groupingBy(BusMemberBalanceLog::getMemberId));
            // 存储会员卡号id和该卡号结账时消费的金额
            Map<String, BigDecimal> vipMoneyMap = new HashMap<>();
            balanceLogMap.forEach((vipId, logs) -> {
                BigDecimal money = logs.stream().map(BusMemberBalanceLog::getMoney).reduce(BigDecimal.ZERO, BigDecimal::add);
                vipMoneyMap.put(vipId, money);
            });
            Set<String> vipIds = vipMoneyMap.keySet();
            List<BusMemberCard> busMemberCards = memberCardService.listByIds(vipIds);
            busMemberCards.forEach(e -> e.setBalance(e.getBalance().add(vipMoneyMap.get(e.getId()))));
            memberCardService.updateBatchById(busMemberCards);
            memberBalanceLogService.removeBatchByIds(balanceLogs);
        }
        feeService.removeBatchByIds(busOrderFees);
        return true;
    }

    @Override
    public IPage<RoomLivingQueryVo> getLivingPageData(Integer status,String keyw, Page<RoomLivingQueryVo> page,String hotelId) {
        IPage<RoomLivingQueryVo> roomLivings = baseMapper.getPage(page, status, keyw, hotelId);

        roomLivings.getRecords().stream().forEach(e ->{
                if (ObjectUtil.isNotEmpty(e.getRelType())){
                    e.setGroupTag(TeamEnum.val(e.getRelType()).getTitle() + e.getSortIndex());
                }
        });
        return roomLivings;
    }

    @Override
    public IPage<RoomLivingQueryVo> getUnionLivingPageData(String keyw, Page<RoomLivingQueryVo> page,String hotelId) {
        IPage<RoomLivingQueryVo> roomLivings = baseMapper.getUnionPage(page, keyw, hotelId, true, null);
        List<String> bookingIds = roomLivings.getRecords().stream().map(RoomLivingQueryVo::getBookingOrderId).collect(Collectors.toList());
        Page<RoomLivingQueryVo> childPage = new Page<>();
        childPage.setPages(1);
        childPage.setSize(9999);
        List<RoomLivingQueryVo> livingQueryVos = baseMapper.getUnionPage(childPage, keyw, hotelId, false, bookingIds).getRecords();
        Map<String, List<RoomLivingQueryVo>> map = livingQueryVos.stream().collect(Collectors.groupingBy(RoomLivingQueryVo::getBookingOrderId));
        roomLivings.getRecords().forEach(e ->{
            if (ObjectUtil.isNotEmpty(e.getRelType())){
                e.setChildren(map.get(e.getBookingOrderId()));
                e.setGroupTag(TeamEnum.val(e.getRelType()).getTitle() + e.getSortIndex());
            }
        });
        return roomLivings;
    }
    //Integer status,String keyw,String hotelId,String thirdUserId,String layoutId,String customerType, Page<KeLiItemVo> page, Date startTime,Date endTime
    @Override
    public IPage<KeLiItemVo> getLivingOrderKL(Page<KeLiItemVo> page, KedanOrderDto kedanOrderDto) {
        IPage<KeLiItemVo> pRes = baseMapper.getKeLiLiving(page, kedanOrderDto);
        List<String> livingIds = pRes.getRecords().stream().map(KeLiItemVo::getId).collect(Collectors.toList());
        List<BusLivingLayoutDayPrice> prices = !livingIds.isEmpty() ? livingLayoutDayPriceService.list(Wrappers.<BusLivingLayoutDayPrice>query()
        .in("living_order_id",livingIds).orderByAsc("day_time")) : new ArrayList<>();
        pRes.getRecords().forEach(s->{
            List<BusLivingLayoutDayPrice> orderDayPrice = prices.stream().filter(a->a.getLivingOrderId().equals(s.getId())).collect(Collectors.toList());
            s.setLivingDayPrices(orderDayPrice);
        });
        return pRes;
    }
    @Override
    public IPage<KeLiItemVo> getBookingOrderKL(Page<KeLiItemVo> page, KedanOrderDto kedanOrderDto) {
        IPage<KeLiItemVo> pRes = baseMapper.getKeBooking(page, kedanOrderDto);
        List<String> livingIds = pRes.getRecords().stream().map(KeLiItemVo::getId).collect(Collectors.toList());
        List<BusBookingLayoutDayPrice> prices = !livingIds.isEmpty() ? dayPriceService.list(Wrappers.<BusBookingLayoutDayPrice>query()
                .in("booking_order_id",livingIds).orderByAsc("day_time")) : new ArrayList<>();
        pRes.getRecords().forEach(s->{
            List<BusBookingLayoutDayPrice> orderDayPrice = prices.stream().filter(a->a.getBookingOrderId().equals(s.getId()) && a.getRoomLayoutId().equals(s.getLayoutId())).collect(Collectors.toList());
            s.setBookingDayPrices(orderDayPrice);
        });
        return pRes;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean setLivingOrderFee(List<BusOrderFee> fees, String livingOrderId,String hotelId) {
        if(fees == null || fees.size() == 0) throw new JeecgBootException("参数无效");
        BusRoomsLivingOrder livingOrder = roomsLivingOrderService.getById(livingOrderId);
        if(livingOrder == null) throw new JeecgBootException("订单不存在");
        BusBookingRooms bookingRoom = bookingRoomsService.getById(livingOrder.getBookingRoomId());
        if(bookingRoom == null) throw new JeecgBootException("未找到房间关联");
        List<BusOrderFeeGoods> orderFeeGoodList = new ArrayList<>();
        List<CesGoods> goodList = new ArrayList<>();
        fees.forEach(roomFee->{
            roomFee.setLivingOrderId(livingOrderId);
            roomFee.setRoomId(bookingRoom.getRoomId());
            roomFee.setFeeType(1); // 消费
            roomFee.setCreateTime(new Date());
            roomFee.setDayTime(new Date());
            roomFee.setHotelId(hotelId);
            feeService.save(roomFee);
            if(roomFee.getSubjectType().equals(FeeSubjectType.SHANG_PIN.getKey()) && roomFee.getFeeGoodVo() != null) {
                roomFee.getFeeGoodVo().setOrderFeeId(roomFee.getId());
                roomFee.getFeeGoodVo().setLivingOrderId(livingOrderId);
                roomFee.getFeeGoodVo().setCreateTime(new Date());
                roomFee.getFeeGoodVo().setHotelId(livingOrder.getHotelId());
                orderFeeGoodList.add(roomFee.getFeeGoodVo());
                CesGoods goods = cesGoodsServiceImpl.getById(roomFee.getFeeGoodVo().getGoodsId());
                Integer newInventory = goods.getInventory() - roomFee.getFeeGoodVo().getNum();
                if (newInventory < 0){
                    throw new JeecgBootException("库存量不足");
                }
                goods.setInventory(newInventory);
                goodList.add(goods);
                KcDepositoryInGoods depositoryInGoods = addDepositoryInGood(roomFee.getFeeGoodVo());

                depositoryInGoodsService.addSell(depositoryInGoods);

            }
        });
        cesGoodsServiceImpl.updateBatchById(goodList);
        orderFeeGoodsService.saveBatch(orderFeeGoodList);
        return true;
    }

    public KcDepositoryInGoods addDepositoryInGood(BusOrderFeeGoodsVo feeGood){
        KcDepositoryInGoods depositoryInGoods = new KcDepositoryInGoods();
        depositoryInGoods.setGoodsId(feeGood.getGoodsId());
        depositoryInGoods.setNum(feeGood.getNum());
        depositoryInGoods.setFeeId(feeGood.getOrderFeeId());
        depositoryInGoods.setHotelId(feeGood.getHotelId());
        depositoryInGoods.setTenantId(TokenUtils.getAuthUser().getRelTenantIds());
        depositoryInGoods.setCreateTime(new Date());
        return depositoryInGoods;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean unionOrder(String roomId,String targetRoomId, String bookingOrderId, String hotelId) {
        CesRooms room = roomsService.getById(roomId);
        if(room == null) throw new JeecgBootException("房间不存在");
        CesRoomLayout layout = layoutService.getById(room.getLayoutId());
        if(layout == null) throw new JeecgBootException("房型不存在");
        BusRoomBookingOrders bkOrder = getById(bookingOrderId);
        if(bkOrder == null) throw new JeecgBootException("订单不存在");
        BusBookingRooms bookingRooms = new BusBookingRooms();
        bookingRooms.setBookingOrdersId(bookingOrderId);
        bookingRooms.setBookingType(bkOrder.getBookingOrdersType());
        bookingRooms.setRoomId(roomId);
        bookingRooms.setRoomStatus(RoomStatusEnum.LIVE_CLEAR.getKey());
        bookingRooms.setLayoutId(layout.getId());
        bookingRooms.setRoomLayoutId(layout.getId());
        CesRooms livingRoom = roomsService.getById(targetRoomId);
        if(livingRoom == null) throw new JeecgBootException("入住房间订单不存在");
        if(livingRoom.getLivingOrderId() == null || livingRoom.getLivingOrderId().isEmpty()) throw new JeecgBootException("非正常入住的订单无法联房");
        BusRoomsLivingOrder roomLivingOrder = roomsLivingOrderService.getById(livingRoom.getLivingOrderId());
        if(!roomLivingOrder.getBookingOrderId().equals(bookingOrderId)) throw new JeecgBootException("订单关联错误");

        BusLivingCustomer livingCustomer =  livingCustomerService.getOne(Wrappers.<BusLivingCustomer>query().eq("living_order_id",roomLivingOrder.getId()).eq("customer_id",roomLivingOrder.getContactId()));
        if(livingCustomer == null) {
            livingCustomer = livingCustomerService.getOne(Wrappers.<BusLivingCustomer>query().eq("living_order_id",roomLivingOrder.getId()));
        }
        if(livingCustomer == null) throw new JeecgBootException("获取客户信息失败");
        bookingRoomsService.save(bookingRooms);
        List<BusLivingCustomer> customers = new ArrayList<>();
        livingCustomer.setId(null);
        livingCustomer.setLivingOrderId(null);
        customers.add(livingCustomer);
        String livingOrderId = bookingToLive(bookingRooms.getId(), customers,hotelId,null, null,null);

        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean mergeOrder(String hotelId, String mainLivingOrderId, List<String> livingOrderId) {
        BusRoomsLivingOrder mainLivingOrder = roomsLivingOrderService.getById(mainLivingOrderId);
        livingOrderId.forEach(s->{
            this.mergeSingle(mainLivingOrder,s);
        });
        List<BusTeam> teams = teamService.list(Wrappers.<BusTeam>lambdaQuery().eq(BusTeam::getBookingOrderId, mainLivingOrder.getBookingOrderId()).eq(BusTeam::getIsDelete, "0"));
        if (teams.isEmpty()){
            addBusTeams(hotelId, mainLivingOrder.getBookingOrderId());
        }
        return true;
    }

    @SneakyThrows
    @Override
    public ForwardFangTaiVo forwardFangTai(String start, String end, String hotelId,Boolean isContainLeave) {
        List<ForwardFangTaiQueryVo> vos = baseMapper.forwardFangTaiQuery(hotelId,start,end);
        isContainLeave = isContainLeave == null?false:isContainLeave;
        List<String> dayStrs = MxTools.findEveryDay(start,end);
        ForwardFangTaiVo result = new ForwardFangTaiVo();
        List<ForwardFangTaiDateVo> dateList = new ArrayList<>();
        List<LayoutRoomCountVo> roomCountVos = baseMapper.getLayoutRoomCount(hotelId);
        List<FwRoomLockVo> roomLockList = fwRoomLockService.detailList();
        List<FwRoomRepairVo> roomRepairList = fwRoomRepairService.detailList();
//        fwRoomRepairService.list()
        Boolean finalIsContainLeave = isContainLeave;

        roomCountVos.forEach(t-> {
            List<FwRoomLockVo> roomLockVos = roomLockList.stream().filter(lock -> StrUtil.equals(t.getLayoutId(), lock.getLayoutId())).collect(Collectors.toList());
            dayStrs.forEach(s-> {

                Date date = null;
                try {
                    date = DateUtils.parseDate(s, "yyyy-MM-dd");
                } catch (ParseException e) {
                    throw new RuntimeException(e);
                }
                Calendar calendar = Calendar.getInstance();
                calendar.setTime(date);
                calendar.add(Calendar.DAY_OF_YEAR,1);
                Date nextDate = calendar.getTime();
                ForwardFangTaiDateVo tableCell = new ForwardFangTaiDateVo();
                tableCell.setDate(s);
                tableCell.setLayoutId(t.getLayoutId());
                List<ForwardFangTaiQueryVo> leaveOrders = new ArrayList<>();
                List<ForwardFangTaiQueryVo> livingOrders = new ArrayList<>();
                final int[] dayLeave = {0};
                final int[] dayLiving = {0};
                // 在住和预约
                vos.forEach(c->{
                    try {
                        long enter = DateUtils.parseDate(c.getEnterDate(),"yyyy-MM-dd").getTime();
                        long leave = DateUtils.parseDate(c.getLeaveDate(),"yyyy-MM-dd").getTime();
                        long curr = DateUtils.parseDate(s,"yyyy-MM-dd").getTime();

                        if(c.getLayoutId().equals(t.getLayoutId())
                                && c.getLeaveDate().equals(s)) {
                            dayLeave[0] += 1;
                            leaveOrders.add(c);

                        }
                        boolean isContainLeaveCondition = enter <= curr && (  finalIsContainLeave.booleanValue()?
                                leave>=curr:leave>curr);// 是否包含今日预离
                        if(c.getLayoutId().equals(t.getLayoutId())
                                && isContainLeaveCondition ) {
                            dayLiving[0] += 1;
                            livingOrders.add(c);
                        }
                    } catch (ParseException e) {
                        e.printStackTrace();
                    }
                });
                // 维修
                Date finalDate = date;
                roomRepairList.forEach(repair -> {
                    if (StrUtil.equals(t.getLayoutId(), repair.getLayoutId()) && repair.getStartDate().compareTo(finalDate) <= 0
                            && repair.getEndDate().compareTo(nextDate) >= 0){
                        ForwardFangTaiQueryVo repairForwardFangTai = new ForwardFangTaiQueryVo();
                        repairForwardFangTai.setRoomId(repair.getRoomId());
                        repairForwardFangTai.setRoomName(repair.getRoomName());
                        repairForwardFangTai.setArrivalTime(repair.getStartDate());
                        repairForwardFangTai.setDueOutTime(repair.getEndDate());
                        repairForwardFangTai.setLayoutId(repair.getLayoutId());
                        repairForwardFangTai.setLayoutName(repair.getLayoutName());
                        repairForwardFangTai.setIsRepair(repair.getIsRepair());
                        dayLiving[0] += 1;
                        livingOrders.add(repairForwardFangTai);
                    }
                });
                // 锁房
                roomLockVos.forEach(lock -> {
                    ForwardFangTaiQueryVo lockForwardFangTai = new ForwardFangTaiQueryVo();
                    lockForwardFangTai.setRoomId(lock.getRoomId());
                    lockForwardFangTai.setRoomName(lock.getRoomName());
                    lockForwardFangTai.setLayoutId(lock.getLayoutId());
                    lockForwardFangTai.setLayoutName(lock.getLayoutName());
                    lockForwardFangTai.setIsLock(lock.getIsLock());
                    dayLiving[0] += 1;
                    livingOrders.add(lockForwardFangTai);
                });
                tableCell.setLeaveCount(dayLeave[0]);// 当天该房型预离数量
                tableCell.setLeaveOrders(leaveOrders);// 当天预离订单
                tableCell.setLivingCount(dayLiving[0]);// 当天在住数量
                tableCell.setLivingOrders(livingOrders);// 当天该房型在住订单
                dateList.add(tableCell);
            });
        });
        result.setDateList(dateList);
        result.setLayoutRooms(roomCountVos);


        return result;
    }
    @Override
    public RiLiFangTaiResultVo riliFangTai(String start, String end, String hotelId) {
        RiLiFangTaiResultVo result = new RiLiFangTaiResultVo();
        List<RiLiFangTaiVo> riLiFangTaiVos = baseMapper.getRiliFangTai(start,end,hotelId);
        result.setRiLiFangTaiVoList(riLiFangTaiVos);
        List<CesRoomLayout> layouts = layoutService.list(Wrappers.<CesRoomLayout>query()
        .eq("hotel_id",hotelId).eq("invalid",false));
        List<CesRooms> rooms = roomsService.list(Wrappers.<CesRooms>query().eq("hotel_id",hotelId).eq("invalid",false));
        List<CesRoomBuildingFloor> buildingFloors =  buildingFloorService.list(Wrappers.<CesRoomBuildingFloor>query().eq("hotelId",hotelId).eq("invalid",false));
        rooms.forEach(s-> {
            Optional<CesRoomLayout> opFindLayout = layouts.stream().filter(a->a.getId().equals(s.getLayoutId())).findFirst();
            if(!opFindLayout.isPresent()) return;
            CesRoomLayout layout = opFindLayout.get();
            s.setMarketPrice(layout.getMarketPrice());
            s.setLayoutName(layout.getName());
        });
        List<RiLiLayoutRoomVo> layoutRoomVos = layouts.stream().map(s->{
            RiLiLayoutRoomVo item = new RiLiLayoutRoomVo();
            item.setId(s.getId());
            item.setName(s.getName());
            item.setRooms(rooms.stream().filter(a->a.getLayoutId().equals(s.getId())).collect(Collectors.toList()));
            return  item;
        }).collect(Collectors.toList());
        List<RiLiFloorRoomVo> floorRoomVos = buildingFloors.stream().filter(s->!s.getParentId().equals("0")).map(s->{
            Optional<CesRoomBuildingFloor> floorOptional = buildingFloors.stream().filter(a->a.getId().equals(s.getParentId())).findFirst();
            if(!floorOptional.isPresent()) return null;
            CesRoomBuildingFloor building = floorOptional.get();
            RiLiFloorRoomVo item = new RiLiFloorRoomVo();
            item.setId(s.getId());
            item.setName(building.getName() + "-" +s.getName());
            item.setRooms(rooms.stream().filter(a->a.getFloorId().equals(s.getId())).collect(Collectors.toList()));
            return  item;
        }).collect(Collectors.toList());
        result.setFloorRoomVos(floorRoomVos);
        result.setLayoutRoomVos(layoutRoomVos);
        return  result;
    }

    @SneakyThrows
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean updateOrderInfo(Integer type, UpdateOrderInfoDto info){
        if(info == null || info.getOrderId() == null || info.getOrderId().isEmpty()) throw new JeecgBootException("参数错误");
        LoginUser user = TokenUtils.getAuthUser();
        // 预约单
        if(type.equals(1)) {
            BusRoomBookingOrders order = getById(info.getOrderId());
            if(order == null) throw new JeecgBootException("订单未找到");
            if(info.getCusName() != null && !info.getCusName().isEmpty() && info.getCusPhone() != null && !info.getCusPhone().isEmpty()){
                BusCustomer customer = customerService.getOne(Wrappers.<BusCustomer>query().eq("name",info.getCusName())
                                .eq("hotel_id",order.getHotelId())
                        .eq("phone",info.getCusPhone()));
                if(customer == null) {
                    // save customer
                    BusCustomer newCustomer = new BusCustomer();
                    newCustomer.setCreateTime(new Date());
                    newCustomer.setCreateUser(user.getId());
                    newCustomer.setHotelId(order.getHotelId());
                    newCustomer.setTenantId(user.getRelTenantIds());
                    newCustomer.setName(info.getCusName());
                    newCustomer.setPhone(info.getCusPhone());
                    newCustomer.setCertType(1); // 身份证
                    customerService.save(newCustomer);
                    order.setContactId(newCustomer.getId());
                } else {
                    order.setContactId(customer.getId());
                }
                updateById(order);
                return true;
            }

            if(info.getCustomerSource() != null && !info.getCustomerSource().isEmpty()) {
                order.setCustomerSource(info.getCustomerSource());
                updateById(order);
                return true;
            }

            if(info.getRemark() != null && !info.getRemark().isEmpty()) {
                order.setRemark(info.getRemark());
                updateById(order);
                return true;
            }
            if(info.getWarranter() != null && !info.getWarranter().isEmpty()) {
                order.setWarranter(info.getWarranter());
                updateById(order);
                return true;
            }

            if(info.getCustomerType() != null && CustomerTypeEnum.val(info.getCustomerType()) != null) {
                order.setCustomerType(info.getCustomerType());
                updateById(order);
                return true;
            }

            if(info.getLiveType() != null && info.getLiveType().equals(2)){
                if(info.getFangAnId() != null && !info.getFangAnId().isEmpty()) {
                    CesHourRoomRule hourRoomRule = hourRoomRuleService.getById(info.getFangAnId());
                    Integer timeMins = hourRoomRule.getAfterOpenRoom();// 分钟
                    Date targetTime = DateUtils.formatAddDateMin(order.getArrivalTime(),timeMins);
                    if(targetTime.getTime() > new Date().getTime()) throw new JeecgBootException("方案时间超时");
                    order.setBookingType(2);
                    order.setHourRoomId(info.getFangAnId());
                    updateById(order);
                } else throw new JeecgBootException("请选择时租房规则");
            }

            if(info.getBrkfstNum() != null && info.getBrkfstNum() >= 0) {
                order.setBreakfastNum(info.getBrkfstNum());
                updateById(order);
                return true;
            }

            if(info.getLchNum() != null && info.getLchNum() >= 0) {
                order.setLunchNum(info.getLchNum());
                updateById(order);
                return true;
            }

            if(info.getDnrNum() != null && info.getDnrNum() >= 0) {
                order.setDinnerNum(info.getDnrNum());
                updateById(order);
                return true;
            }

            return false;
        }
        if(type.equals(2)) {
            BusRoomsLivingOrder order = roomsLivingOrderService.getById(info.getOrderId());
            if(order == null) throw new JeecgBootException("订单未找到");
            if(info.getCusName() != null && !info.getCusName().isEmpty() && info.getCusPhone() != null && !info.getCusPhone().isEmpty()){
                BusCustomer customer = customerService.getOne(Wrappers.<BusCustomer>query().eq("name",info.getCusName())
                                .eq("hotel_id",order.getHotelId())
                        .eq("phone",info.getCusPhone()));
                if(customer == null) {
                    // save customer
                    BusCustomer newCustomer = new BusCustomer();
                    newCustomer.setCreateTime(new Date());
                    newCustomer.setCreateUser(user.getId());
                    newCustomer.setHotelId(order.getHotelId());
                    newCustomer.setTenantId(user.getRelTenantIds());
                    newCustomer.setName(info.getCusName());
                    newCustomer.setPhone(info.getCusPhone());
                    newCustomer.setGender(info.getGender());
                    newCustomer.setCertNo(info.getCertNo());
                    newCustomer.setCertType(info.getCertType());
                    newCustomer.setAddress(info.getAddress());
                    newCustomer.setNation(info.getNation());
                    customerService.save(newCustomer);
                    customer = newCustomer;
                    order.setContactId(newCustomer.getId());
                } else {
                    order.setContactId(customer.getId());
                }
                List<BusLivingCustomer> customers = livingCustomerService.list(Wrappers.<BusLivingCustomer>query().eq("living_order_id",order.getId()));
                if(customers.size() > 0) {
                    BusLivingCustomer firstCustomer = customers.get(0);
                    firstCustomer.setCustomerId(customer.getId());
                    firstCustomer.setCustomerName(customer.getName());
                    firstCustomer.setGender(customer.getGender());
                    firstCustomer.setPhone(customer.getPhone());
                    firstCustomer.setCertType(customer.getCertType());
                    firstCustomer.setCertNo(customer.getCertNo());
                    livingCustomerService.updateById(firstCustomer);
                }
                roomsLivingOrderService.updateById(order);
                return true;
            }

            if(info.getCustomerSource() != null && !info.getCustomerSource().isEmpty()) {
                order.setCustomerSource(info.getCustomerSource());
                roomsLivingOrderService.updateById(order);
                return true;
            }

            if(info.getRemark() != null && !info.getRemark().isEmpty()) {
                order.setRemark(info.getRemark());
                roomsLivingOrderService.updateById(order);
                return true;
            }
            if(info.getWarranter() != null && !info.getWarranter().isEmpty()) {
                order.setWarranter(info.getWarranter());
                roomsLivingOrderService.updateById(order);
                return true;
            }

            if(info.getCustomerType() != null && CustomerTypeEnum.val(info.getCustomerType()) != null) {

                // 如果改变的客人类型有会员卡
                BigDecimal changeDiscount = new BigDecimal(1);
                String vipCustomerId = "";
                String marketAgreementCustomerId = "";
                String contractTeamId = "";
                if (CustomerTypeEnum.VIP.getKey().equals(info.getCustomerType()) && StrUtil.isNotEmpty(info.getVipCustomerId())){
                    BusMemberCard changeMemberCard = memberCardService.getById(info.getVipCustomerId());
                    if (ObjectUtil.isEmpty(changeMemberCard)){
                        throw new JeecgBootException("未找到相应的会员");
                    }
                    BusMarketMember changeMarketMember = marketMemberService.getById(changeMemberCard.getGradeId());
                    if (ObjectUtil.isEmpty(changeMarketMember) || ObjectUtil.isEmpty(changeMarketMember.getDiscount())){
                        throw new JeecgBootException("会员类型异常");
                    }
                    vipCustomerId = changeMemberCard.getId();
                    changeDiscount = changeMarketMember.getDiscount().divide(new BigDecimal(100));
                }

                //
                if (CustomerTypeEnum.PROTOCOL.getKey().equals(info.getCustomerType()) && StrUtil.isNotEmpty(info.getContractTeamProtocolId())){
                    BusMarketAgreementCustomer changeMarketAgreementCustomer = marketAgreementCustomerService.getById(info.getContractTeamProtocolId());

                    if (ObjectUtil.isEmpty(changeMarketAgreementCustomer) || ObjectUtil.isEmpty(changeMarketAgreementCustomer.getDiscount())){
                        throw new JeecgBootException("合同类型异常");
                    }
                    marketAgreementCustomerId = changeMarketAgreementCustomer.getId();
                    contractTeamId = info.getContractTeamId();
                    changeDiscount = changeMarketAgreementCustomer.getDiscount().divide(new BigDecimal(100));
                }

                // 如果原来客人类型有会员卡
                BigDecimal originalDiscount = new BigDecimal(1);
                if (StrUtil.isNotEmpty(order.getVipCustomerId())){
                    BusMemberCard originalMemberCard = memberCardService.getById(order.getVipCustomerId());
                    if (ObjectUtil.isEmpty(originalMemberCard)){
                        throw new JeecgBootException("未找到原相应的会员");
                    }
                    BusMarketMember originalMarketMember = marketMemberService.getById(originalMemberCard.getGradeId());
                    if (ObjectUtil.isEmpty(originalMarketMember) || ObjectUtil.isEmpty(originalMarketMember.getDiscount())){
                        throw new JeecgBootException("原会员类型异常");
                    }
                    originalDiscount = originalMarketMember.getDiscount().divide(new BigDecimal(100));
                }


                // 如果有原合同
                if (StrUtil.isNotEmpty(order.getContractTeamProtocolId())){
                    BusMarketAgreementCustomer marketAgreementCustomer = marketAgreementCustomerService.getById(order.getContractTeamProtocolId());
                    if (ObjectUtil.isEmpty(marketAgreementCustomer) || ObjectUtil.isEmpty(marketAgreementCustomer.getDiscount())){
                        throw new JeecgBootException("原合同类型异常");
                    }
                    originalDiscount = marketAgreementCustomer.getDiscount().divide(new BigDecimal(100));
                }

                order.setCustomerType(info.getCustomerType());
                order.setVipCustomerId(vipCustomerId);
                order.setContractTeamId(contractTeamId);
                order.setContractTeamProtocolId(marketAgreementCustomerId);
                // 找出未结算的房费订单，且不是冲账单 和每日价格，按会员价算
                List<BusOrderFee> orderFeeList = feeService.list(Wrappers.<BusOrderFee>lambdaQuery()
                        .eq(BusOrderFee::getLivingOrderId, order.getId()).eq(BusOrderFee::getSubjectType, FeeSubjectType.MEI_RI_FANG_FEI.getKey())
                        .eq(BusOrderFee::getPreferentialStatus, 1).gt(BusOrderFee::getMoney,0));
                // 原始则扣和改变后的折扣
                BigDecimal finalOriginalDiscount = originalDiscount;
                BigDecimal finalChangeDiscount = changeDiscount;
                orderFeeList.forEach(e -> {
                    e.setMoney(e.getMoney().multiply(finalChangeDiscount).divide(finalOriginalDiscount));
                });
                List<BusLivingLayoutDayPrice> livingDayPrice = livingLayoutDayPriceService.list(Wrappers.<BusLivingLayoutDayPrice>lambdaQuery()
                        .eq(BusLivingLayoutDayPrice::getLivingOrderId, order.getId()));
                livingDayPrice.forEach(e -> {
                    e.setPrice(e.getPrice().multiply(finalChangeDiscount).divide(finalOriginalDiscount));
                });
                feeService.updateBatchById(orderFeeList);
                livingLayoutDayPriceService.updateBatchById(livingDayPrice);
                boolean b = roomsLivingOrderService.updateById(order);
                return true;
            }

            if(info.getLiveType() != null){
                if(info.getPrices() == null || info.getPrices().getDayPrices().size() <= 0) {
                    throw new JeecgBootException("请传入价格");
                }
                if(info.getLiveType().equals(2)) {
                    if(info.getFangAnId() != null && !info.getFangAnId().isEmpty()) {
                        CesHourRoomRule hourRoomRule = hourRoomRuleService.getById(info.getFangAnId());
                        Integer timeMins = hourRoomRule.getAfterOpenRoom();// 分钟
                        Date targetTime = DateUtils.formatAddDateMin(order.getArrivalTime(),timeMins);
                        if(targetTime.getTime() < new Date().getTime()) throw new JeecgBootException("方案时间超时");
                        order.setLivingType(2);
                        order.setHourRoomId(info.getFangAnId());
                        order.setDueOutTime(targetTime);
                        Calendar startCal = Calendar.getInstance();
                        startCal.setTime(order.getArrivalTime());
                        Calendar endCal = Calendar.getInstance();
                        endCal.setTime(targetTime);
                        int dayCount = DateUtils.dateDiff('d',startCal,endCal);
                        order.setDayCount(Math.abs(dayCount));
                    } else throw new JeecgBootException("请选择时租房规则");

                } else if(info.getLiveType().equals(1)) {
                    order.setLivingType(1);
                    CesAllDayPriceRule rule = allDayPriceRuleService.getOne(Wrappers.<CesAllDayPriceRule>query().eq("hotel_id",order.getHotelId()));
                    String leaveTime = "13:00";
                    if(rule == null || rule.getLeaveTime() == null) {
                        leaveTime = rule.getLeaveTime();
                    }
                    String dateStr = DateUtils.formatAddDate(DateUtils.formatDate(new Date())+" "+leaveTime+":00","yyyy-MM-dd HH:mm:ss",info.getPrices().getDayPrices().size()) +" "+leaveTime+":00";
                    order.setDueOutTime(DateUtils.parseDatetime(dateStr));
                }
                updateOrderPrice(info.getPrices());
                long dayCount = livingLayoutDayPriceService.count(Wrappers.<BusLivingLayoutDayPrice>query()
                        .eq("living_order_id", order.getId()));
                order.setDayCount((int)dayCount);
                roomsLivingOrderService.updateById(order);
                return true;
            }

            if(info.getBrkfstNum() != null && info.getBrkfstNum() >= 0) {
                order.setBreakfastNum(info.getBrkfstNum());
                roomsLivingOrderService.updateById(order);
                return true;
            }

            if(info.getLchNum() != null && info.getLchNum() >= 0) {
                order.setLunchNum(info.getLchNum());
                roomsLivingOrderService.updateById(order);
                return true;
            }

            if(info.getDnrNum() != null && info.getDnrNum() >= 0) {
                order.setDinnerNum(info.getDnrNum());
                roomsLivingOrderService.updateById(order);
                return true;
            }
            return false;
        }
        return false;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean updateOrderPrice(UpdateOrderPriceDto info) {
        if(info.getLivingOrderId() == null || info.getLivingOrderId().isEmpty()) throw new JeecgBootException("参数错误");
        if(info.getDayPrices() == null || info.getDayPrices().size() == 0) throw new JeecgBootException("参数错误");
        livingLayoutDayPriceService.remove(Wrappers.<BusLivingLayoutDayPrice>query()
                .ge("day_time", DateUtils.formatDate(new Date()))
                .eq("living_order_id",info.getLivingOrderId()));

        info.getDayPrices().stream().forEach(s-> {
            long stamp1 = 0;
            long stamp2 = 0;
            try{
                stamp1 = DateUtils.parseDate(DateUtils.formatDate(s.getDayTime()) ,"yyyy-MM-dd").getTime();
                stamp2 = DateUtils.parseDate(DateUtils.formatDate(new Date()) ,"yyyy-MM-dd").getTime();

            } catch(Exception error) {
                error.printStackTrace();
            }
            if(stamp1 == 0 || stamp2 == 0 || stamp1 < stamp2) return;
            s.setId(null);
            livingLayoutDayPriceService.save(s);
            if(DateUtils.formatDate(new Date()).equals(DateUtils.formatDate(s.getDayTime()))){
                // 修改当日房价 产生的当日房费账单也要改
                BusOrderFee fee = feeService.getOne(Wrappers.<BusOrderFee>query()
                        .eq("DATE_FORMAT(day_time,'%Y-%m-%d')",DateUtils.formatDate(new Date())).eq("living_order_id",s.getLivingOrderId()));
                if(fee != null) {
                    fee.setMoney(s.getPrice());
                    feeService.updateById(fee);
                }
            }
        });
        return  true;
    }

    public Boolean batchUpdateOrderPrice(List<UpdateOrderPriceDto> infos){
        List<BusLivingLayoutDayPrice> dayPriceList = new ArrayList<>();
        ArrayList<BusOrderFee> orderFeeList = new ArrayList<>();
        infos.forEach(info -> {
            if(info.getLivingOrderId() == null || info.getLivingOrderId().isEmpty()) throw new JeecgBootException("参数错误");
            if(info.getDayPrices() == null || info.getDayPrices().isEmpty()) throw new JeecgBootException("参数错误");
            info.getDayPrices().forEach(s-> {
                long stamp1 = 0;
                long stamp2 = 0;
                try{
                    stamp1 = DateUtils.parseDate(DateUtils.formatDate(s.getDayTime()) ,"yyyy-MM-dd").getTime();
                    stamp2 = DateUtils.parseDate(DateUtils.formatDate(new Date()) ,"yyyy-MM-dd").getTime();
                } catch(Exception error) {
                    error.printStackTrace();
                }
                if(stamp1 == 0 || stamp2 == 0 || stamp1 < stamp2) return;
                dayPriceList.add(s);
                if(DateUtils.formatDate(new Date()).equals(DateUtils.formatDate(s.getDayTime()))){
                    // 修改当日房价 产生的当日房费账单也要改
                    BusOrderFee fee = feeService.getOne(Wrappers.<BusOrderFee>query()
                            .eq("DATE_FORMAT(day_time,'%Y-%m-%d')",DateUtils.formatDate(new Date())).eq("living_order_id",s.getLivingOrderId()));
                    if(fee != null) {
                        fee.setMoney(s.getPrice());
                        orderFeeList.add(fee);
                    }
                }
            });
        });
        boolean updateFee = true;
        boolean updateDayPrice = true;
        if (!orderFeeList.isEmpty()){
            updateFee = feeService.updateBatchById(orderFeeList);
        }
        if (!dayPriceList.isEmpty()){
            updateDayPrice = livingLayoutDayPriceService.updateBatchById(dayPriceList);
        }
        return updateDayPrice && updateFee;
    }
//bookingRooms.getBookingOrdersId()是关联房间号的关联id
    private Boolean mergeSingle(BusRoomsLivingOrder mainLivingOrder, String mergeLivingOrderId ) {
        //通过订单id找到订单
        BusRoomsLivingOrder mergeLivingOrder = roomsLivingOrderService.getById(mergeLivingOrderId);
        if(mergeLivingOrder == null) throw new JeecgBootException("合并的订单不存在");
        if(mergeLivingOrder.getSettleType().equals(SettleTypeEnum.SETTLE_LEAVE.getKey())) throw new JeecgBootException("结账退房的订单无法合并");
        //通过订单关联的房间id找到房间B
        BusBookingRooms bookingRooms = bookingRoomsService.getById(mergeLivingOrder.getBookingRoomId());
        List<BusRoomsLivingOrder> needMergeLivingOrders = new ArrayList<>();
        //主订单
        if(bookingRooms.getIsMain()){
            //找出房间B所有的关联订单，包括了B房间自己
            List<BusRoomsLivingOrder> relationOtherLivingOrders = roomsLivingOrderService.list(Wrappers.<BusRoomsLivingOrder>query()
                    .eq("booking_order_id", bookingRooms.getBookingOrdersId()));
            //所有订单的房间id集合
            List<String> bookingRoomIds = relationOtherLivingOrders.stream().map(s->s.getBookingRoomId()).collect(Collectors.toList());
            //找出B房间所有的关联房间
            List<BusBookingRooms> bookingRoomsList = bookingRoomsService.list(Wrappers.<BusBookingRooms>query().in("id",bookingRoomIds));
            bookingRoomsList.forEach(s->{
                s.setIsMain(false);
                s.setBookingOrdersId(mainLivingOrder.getBookingOrderId());
            });
            //将修改后关联标识后的房间存好
            bookingRoomsService.updateBatchById(bookingRoomsList);
            needMergeLivingOrders.addAll(relationOtherLivingOrders);
        } else {
            needMergeLivingOrders.add(mergeLivingOrder);
        }
        //所有订单都改成主订单的关联标识id
        needMergeLivingOrders.forEach(s->{
            s.setBeforeMergeBookingOrderId(s.getBookingOrderId());
            s.setBookingOrderId(mainLivingOrder.getBookingOrderId());
        });
        roomsLivingOrderService.updateBatchById(needMergeLivingOrders);

        return true;
    }
    @Override
    public void syncDayOrderFee() {
        // 扫描每个订单，生成夜审房费，如果当日没有退房，并且没有生成当日房费，
        // 则生成新的每日房费，生成的房费以门市价为准,
        // 如果超过全天放计费的重算全天放时间，需要更改该订单的预离时间，自动续为全天房 //
        // 未结账的房间集合
        List<BusRoomsLivingOrder> unSettledLivings = roomsLivingOrderService.list(Wrappers.<BusRoomsLivingOrder>query()
         .eq("settle_type", SettleTypeEnum.UN_LEAVE.getKey()));
        List<CesAllDayPriceRule> allDayPriceRules = allDayPriceRuleService.list();
        unSettledLivings.forEach(s->{
            Optional<CesAllDayPriceRule> opAllPrice = allDayPriceRules.stream().filter(a->a.getHotelId().equals(s.getHotelId())).findFirst();
            CesAllDayPriceRule allPrice;
            if(!opAllPrice.isPresent()) {
                allPrice = new CesAllDayPriceRule();
                allPrice.setEndTime("16:00");
            } else {
                allPrice = opAllPrice.get();
            }
            syncDayOrderFeeItem(s,allPrice);
        });

    }
    @Override
    public List<BusRoomBookingOrders> countTodayYD(String hotelId) {
        return  baseMapper.countTodayYD(hotelId);
    }
    @SneakyThrows
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void syncDayOrderFeeItem(BusRoomsLivingOrder order, CesAllDayPriceRule allDayRule) {
        Date currDate = new Date();
        // 當前時間小於轉全天房臨界時間，则不续房
        if(currDate.getTime() < DateUtils.parseDate(DateUtils.formatDate(new Date())+" " + allDayRule.getEndTime(),"yyyy-MM-dd") .getTime()) {
            Calendar calendar = new GregorianCalendar();
            calendar.setTime(currDate);
            // 把日期往后增加一天,整数  往后推,负数往前移动
            calendar.add(Calendar.DATE, -1);
            // 这个时间就是日期往后推一天的结果
            currDate = calendar.getTime();
        }
        List<String> dayStrs = MxTools.findEveryDay(DateUtils.formatTime(order.getArrivalTime()),
                DateUtils.formatTime(currDate));
        BusBookingRooms bookingRoom = bookingRoomsService.getById(order.getBookingRoomId());
        CesRooms room = roomsService.getById(bookingRoom.getRoomId());
        CesRoomLayout layout = layoutService.getById(room.getLayoutId());
        if(bookingRoom == null || room == null || layout == null) return;
        List<BusLivingLayoutDayPrice> dayPrices = livingLayoutDayPriceService.list(Wrappers.<BusLivingLayoutDayPrice>query()
                .eq("living_order_id",order.getId()));
        List<BusOrderFee> dayFees = feeService.list(Wrappers.<BusOrderFee>query()
                .eq("living_order_id",order.getId()).eq("room_id",room.getId()).and(
                        c->c.eq("subject_type",FeeSubjectType.MEI_RI_FANG_FEI.getKey()).or().eq("subject_type",FeeSubjectType.YE_SHEN_FANG_FEI.getKey())
                ));

        // 如果预离时间小于当天 全天房退房时间，默认16:00,则根据全天规则续房为全天房
        if(order.getDueOutTime().getTime() < DateUtils.parseDate(DateUtils.formatDate(new Date())+" " + allDayRule.getEndTime(),"yyyy-MM-dd") .getTime()) {
            Date orderLeave = order.getDueOutTime();
            Calendar calendar = new GregorianCalendar();
            calendar.setTime(orderLeave);
            // 把日期往后增加一天,整数  往后推,负数往前移动
            calendar.add(Calendar.DATE, 1);
            // 这个时间就是日期往后推一天的结果
            orderLeave = calendar.getTime();
            order.setDueOutTime(orderLeave);
            roomsLivingOrderService.updateById(order);
        }
        Date finalCurrDate = currDate;
        dayStrs.forEach(s->{
            try {
                // 如果是当天还没有到退房时间 就不计算当天房费
                if(s.equals(DateUtils.formatDate(finalCurrDate)) && (order.getDueOutTime().getTime() < DateUtils.parseDate(DateUtils.formatDate(new Date())+" " + allDayRule.getEndTime(),"yyyy-MM-dd HH:mm:ss") .getTime())){
                    return;
                }

            } catch (ParseException e) {
                e.printStackTrace();
            }
            Optional<BusLivingLayoutDayPrice> opPrice = dayPrices.stream().filter(a-> DateUtils.formatDate(a.getDayTime()).equals(s)).findFirst();
            if(!opPrice.isPresent()){
                BusLivingLayoutDayPrice nPrice = new BusLivingLayoutDayPrice();
                nPrice.setBookingRoomId(order.getBookingRoomId());
                try {
                    nPrice.setDayTime(DateUtils.parseDate(s,"yyyy-MM-dd"));
                } catch (ParseException e) {
                    e.printStackTrace();
                }
                nPrice.setRoomId(bookingRoom.getRoomId());
                nPrice.setRoomLayoutId(room.getLayoutId());
                nPrice.setPrice(layout.getMarketPrice());
                nPrice.setLivingOrderId(order.getId());
                nPrice.setLivingType(order.getLivingType());

                livingLayoutDayPriceService.save(nPrice);
            }
            Optional<BusOrderFee> opFee = dayFees.stream().filter(a->DateUtils.formatDate(a.getDayTime()).equals(s)).findFirst();
            if(!opFee.isPresent()) {
                BusOrderFee nFee = new BusOrderFee();
                nFee.setMoney(layout.getMarketPrice());
                nFee.setCustorerOrderRemark("每日房费");
                nFee.setAllowCouponscash(false);
                nFee.setSubjectType(FeeSubjectType.MEI_RI_FANG_FEI.getKey());
                nFee.setHotelId(order.getHotelId());
                nFee.setCreateTime(new Date());
                try {
                    nFee.setDayTime(DateUtils.parseDate(s,"yyyy-MM-dd"));
                } catch (ParseException e) {
                    e.printStackTrace();
                }
                nFee.setRoomId(room.getId());
                nFee.setFeeType(1);
                nFee.setPreferentialStatus(1);
                nFee.setLivingOrderId(order.getId());
                try {
                    if(DateUtils.parseDate(s,"yyyy-MM-dd").getTime() < DateUtils.parseDate(DateUtils.formatDate(finalCurrDate),"yyyy-MM-dd").getTime()) {
                        nFee.setCustorerOrderRemark("夜审房费");
                        nFee.setSubjectType(FeeSubjectType.YE_SHEN_FANG_FEI.getKey());
                    }
                } catch (ParseException e) {
                    e.printStackTrace();
                }
                feeService.save(nFee);
            } else {
                BusOrderFee nFee = opFee.get();
                try {
                    // 每日房费转夜审房费
                    if(nFee.getSubjectType().equals(FeeSubjectType.MEI_RI_FANG_FEI.getKey()) &&  DateUtils.parseDate(s,"yyyy-MM-dd").getTime() < DateUtils.parseDate(DateUtils.formatDate(finalCurrDate),"yyyy-MM-dd").getTime()) {
                        nFee.setCustorerOrderRemark("夜审房费");
                        nFee.setRemark("当日房费 ——> 夜审房费");
                        nFee.setSubjectType(FeeSubjectType.YE_SHEN_FANG_FEI.getKey());
                        feeService.updateById(nFee);
                    }
                } catch (ParseException e) {
                    e.printStackTrace();
                }
            }
        });
    }


    String getBookingRoomHotelId(BusBookingRooms bookingRoom) {
        return roomsService.getById(bookingRoom.getRoomId()).getHotelId();
    }
    private void settleFee(BusBookingRooms mainRoom, BusRoomsLivingOrder mainRoomOrder, List<BusOrderFee> feeItems, List<BusOrderFee> settleFees) {
        if(settleFees.isEmpty()) throw new JeecgBootException("参数错误");
        String hotelId =  getBookingRoomHotelId(mainRoom);
        LoginUser user = TokenUtils.getAuthUser();
//        if(user == null) throw new JeecgBootException("请登录");
        if(hotelId == null) throw  new JeecgBootException("未找到酒店");
        List<BusRoomPayType> payTypes = payTypeService.list();
        // 如果未结算的只有一条，且是消费单（部分结账的时候）。收款单中关联这条消费单
        String consumeFeeId;
        if (feeItems != null && feeItems.size() == 1 && feeItems.get(0).getFeeType().equals(1)){
            consumeFeeId = feeItems.get(0).getId();
        } else {
            consumeFeeId = null;
        }
        settleFees.forEach(s->{
            if(BooleanUtil.isTrue(s.getIsPreferential())) {
                if(s.getCouponscashId() != null && !s.getCouponscashId().isEmpty()) {
                    BusMarketCouponsCashUsed couponTicket = marketCouponsCashUsedService.getById(s.getCouponscashId()); // 优惠票券
                    if(couponTicket == null) throw new JeecgBootException("优惠券不存在");
                    if(!couponTicket.getStatus().equals(CouponsStatusEnum.received.getKey())) {
                        CouponsStatusEnum couponStatusText = CouponsStatusEnum.val(couponTicket.getStatus());
                        if(couponStatusText == null) throw new JeecgBootException("优惠券状态异常");
                        throw new JeecgBootException("优惠券"+ couponStatusText.getTitle());
                    } else {
                        couponTicket.setStatus(CouponsStatusEnum.used.getKey());
                        marketCouponsCashUsedService.updateById(couponTicket);
                    }
                }
                s.setFeeType(1);
                s.setSubjectType(FeeSubjectType.YOU_HUI.getKey());
                s.setPayType(null);
            } else {
                String payTypeId = s.getPayType();
                if(payTypeId != null && !payTypeId.isEmpty()) {
                    Optional<BusRoomPayType> opPayType = payTypes.stream().filter(a->a.getId().equals(payTypeId)).findAny();
                    if(!opPayType.isPresent()) throw new JeecgBootException("收款支付方式错误");
                    BusRoomPayType payType = opPayType.get();
                    // 会员卡付费
                    if(payType.getName().equals("会员卡")) {
                        if(mainRoomOrder.getCustomerType() == null || !mainRoomOrder.getCustomerType().equals(CustomerTypeEnum.VIP.getKey()))
                            throw new JeecgBootException("客人类型不是会员");
                        if(mainRoomOrder.getVipCustomerId() == null || mainRoomOrder.getVipCustomerId().isEmpty())
                            throw new JeecgBootException("会员不存在");
                        BusMemberCard vipCard = memberCardService.getById(mainRoomOrder.getVipCustomerId());
                        if(vipCard == null) throw new JeecgBootException("会员不存在");
                        if(vipCard.getBalance().compareTo(s.getMoney()) < 0) throw new JeecgBootException("会员卡余额不足");
                        vipCard.setBalance(vipCard.getBalance().subtract(s.getMoney()));
                        memberCardService.updateById(vipCard);
                        // log
                        s.setId(String.valueOf(IdWorker.getId()));
                        BusMemberBalanceLog balanceLog = new BusMemberBalanceLog();
                        balanceLog.setOrderFeeId(s.getId());
                        balanceLog.setMoney(s.getMoney());
                        balanceLog.setBalance(vipCard.getBalance());
                        balanceLog.setType(2);// 扣费
                        balanceLog.setMemberId(mainRoomOrder.getVipCustomerId());
                        balanceLog.setStatus(1);// TODO：不知道干什么用的 貌似是个啥支付状态
                        balanceLog.setGiveMoney(new BigDecimal(0));
                        String remark = s.getMoney() != null && s.getMoney().compareTo(BigDecimal.valueOf(0)) > 0 ? "房间收费扣款" : "房间收费收款";
                        balanceLog.setRemarks(remark);
                        balanceLog.setHotelId(hotelId);
                        if(user!=null) {
                            balanceLog.setTenantId(user.getRelTenantIds());
                        }
                        balanceLog.setPaymentMethod("");
                        balanceLog.setPayMoney(new BigDecimal(0));
                        balanceLog.setCode(BusMemberBalanceLogServiceImpl.randomNumber("KF"));
                        balanceLog.setCreateTime(new Date());
                        memberBalanceLogService.save(balanceLog);
                        s.setVipCardId(vipCard.getId());
                        // todo 积分规则
                    }
                }
                s.setFeeType(2);
                s.setSubjectType(FeeSubjectType.JIE_ZHANG.getKey());
                s.setContactId(mainRoomOrder.getContactId());
                s.setConsumeFeeId(consumeFeeId);
            }
            s.setPreferentialStatus(2);
            s.setCreateTime(new Date());
            s.setRoomId(mainRoom.getRoomId());
            s.setLivingOrderId(mainRoomOrder.getId());
            s.setHotelId(hotelId);
        });
        if(!settleFees.isEmpty()) {
            // 保存收款单
            feeService.saveBatch(settleFees);
        } else throw new JeecgBootException("参数没有结账收费项目");
        // 全部结账时是所有未结算的账单 ，部分结账时是勾选的消费账单
        feeItems.forEach(s->{
            s.setPreferentialStatus(2);
        });
        feeService.updateBatchById(feeItems);
    }
    private List<BusOrderFee> getLivingOrderFees(String livingOrderId) {
        return feeService.list(Wrappers.<BusOrderFee>query()
        .eq("living_order_id",livingOrderId));
    }
    private Map<String,String> bookingRoomToLiving(BookingOrderSaveDto livingData, List<ExtendBusBookingRoomsVo> roomIds, BookingOrderEditVo bookingOrderVo,String hotelId) {
        Map<String,String> results = new HashMap<>();
        roomIds.forEach(bkRoom-> {
            if(livingData.getRoomIds().stream().filter(s->s.getRoomId().equals(bkRoom.getRoomId())).count() == 0)
                throw new JeecgBootException("房间信息未找到");
            BookingLayoutRoomsDto bkRoomItem = livingData.getRoomIds().stream().filter(s->s.getRoomId().equals(bkRoom.getRoomId())).findFirst().get();
            List<BusLivingCustomer> customers = bkRoomItem.getLivingCustomers();
            if(customers == null || customers.isEmpty()) throw new JeecgBootException("请添加客户信息");

            // 保存入住单，修改房间状态
            String livingOrderId = bookingToLive(bkRoom.getId(), customers,hotelId,null, bkRoomItem, livingData.getLivingRoomDayPrices());
            // 将预约时交的押金，放到主房
            if (bkRoom.getIsMain()){
                LambdaUpdateWrapper<BusOrderFee> updateWrapper = new LambdaUpdateWrapper<>();
                updateWrapper.set(BusOrderFee::getLivingOrderId, livingOrderId);
                updateWrapper.set(BusOrderFee::getRoomId, bkRoom.getRoomId());
                updateWrapper.eq(BusOrderFee::getBookingOrderId, bkRoom.getBookingOrdersId());
                updateWrapper.eq(BusOrderFee::getBookingYj, true);
                updateWrapper.isNull(BusOrderFee::getLivingOrderId);
                feeService.update(updateWrapper);
            }

            // 费用关联到主房
            if(livingData.getOrderFees()!=null && !livingData.getOrderFees().isEmpty()) {
                List<BusOrderFee> findRoomFees = livingData.getOrderFees().stream().filter(t->t.getRoomId().equals(bkRoom.getRoomId())).collect(Collectors.toList());
                findRoomFees.forEach(s->{
                    s.setRoomId(bkRoom.getRoomId());
                    s.setLivingOrderId(livingOrderId);
                    s.setCreateTime(new Date());
                    s.setFeeType(2);// 收款
                    s.setHotelId(hotelId);
                    s.setVipCardId(livingData.getOrderInfo().getVipCustomerId());
                    if(FeeSubjectType.val(s.getSubjectType()) == null) {
                        throw new JeecgBootException("收款项目不存在，请核对后操作");
                    }
                });
                feeService.collectionBatch(findRoomFees, hotelId);
            }

            // 另外每个房间还要在入住时生成每日房费账单
//            BusOrderFee roomFee = new BusOrderFee();
//            roomFee.setRoomId(bkRoom.getRoomId());
//            roomFee.setLivingOrderId(livingOrderId);
//            roomFee.setFeeType(1); // 消费
//            List<BusLivingLayoutDayPrice> roomDayPrice = livingData.getLivingRoomDayPrices().stream()
//                    .filter(i->i.getRoomId().equals(bkRoom.getRoomId())).collect(Collectors.toList());
//            BigDecimal dayRoomFee =  currentDayFee(roomDayPrice,null);
//            if(dayRoomFee == null) throw new JeecgBootException("未找到当日房价");
//            roomFee.setMoney(dayRoomFee); // 取当日房费的优惠价  其他时间要查询房价方案、每日房价，房型门市价按照规则去取
//            roomFee.setCreateTime(new Date());
//            roomFee.setDayTime(new Date());
//            roomFee.setHotelId(hotelId);
//            roomFee.setSubjectType(FeeSubjectType.MEI_RI_FANG_FEI.getKey());
//            feeService.save(roomFee);
            results.put(bkRoom.getRoomId(),livingOrderId);
        });
        return results;
    }
    private BigDecimal currentDayFee(List<BusLivingLayoutDayPrice> dayPrices, BigDecimal defaultPrice){
        Optional<BusLivingLayoutDayPrice> opDayPrice = dayPrices.stream().filter(
                s-> DateUtils.formatDate(s.getDayTime())
                        .equals(DateUtils.formatDate(new Date())
        )).findFirst();
        if(opDayPrice.isPresent()) {
            return opDayPrice.get().getPrice();
        } else {
            return defaultPrice;
        }
    }
    private BusRoomsLivingOrder copyBookingToAcceptOrder(BusRoomBookingOrders bkOrder) {
        BusRoomsLivingOrder livingOrder = new BusRoomsLivingOrder();
        livingOrder.setAgencyName(bkOrder.getAgencyName());
        livingOrder.setArrivalTime(bkOrder.getArrivalTime());
        livingOrder.setBreakfastNum(bkOrder.getBreakfastNum());
        livingOrder.setContactId(bkOrder.getContactId());
        livingOrder.setCreateTime(new Date());
        livingOrder.setCreateUser("");
        livingOrder.setCustomerSource(bkOrder.getCustomerSource());
        livingOrder.setLivingOrdersType(bkOrder.getBookingOrdersType());
        livingOrder.setLivingType(bkOrder.getBookingType());
        livingOrder.setOuterOrdersNo(bkOrder.getOuterOrdersNo());
        livingOrder.setContractTeamId(bkOrder.getContractTeamId());
        livingOrder.setContractTeamProtocolId(bkOrder.getContractTeamProtocolId());
        livingOrder.setDayCount(bkOrder.getDayCount());
        livingOrder.setCustomerType(bkOrder.getCustomerType());
        livingOrder.setDinnerNum(bkOrder.getDinnerNum());
        livingOrder.setLunchNum(bkOrder.getLunchNum());
        livingOrder.setDueOutTime(bkOrder.getDueOutTime());
        livingOrder.setHourRoomId(bkOrder.getHourRoomId());
        livingOrder.setWarranter(bkOrder.getWarranter());
        livingOrder.setWarrantType(bkOrder.getWarrantType());
        livingOrder.setVipCustomerId(bkOrder.getVipCustomerId());
        livingOrder.setTeamName(bkOrder.getTeamName());
        livingOrder.setTeamTypeDicId(bkOrder.getTeamTypeDicId());
        livingOrder.setRoomPriceSlnId(bkOrder.getRoomPriceSlnId());
        livingOrder.setLongSchemeId(bkOrder.getLongSchemeId());
        livingOrder.setLeaderName(bkOrder.getLeaderName());
        livingOrder.setPublicConsumeFee(bkOrder.getPublicConsumeFee());
        livingOrder.setPublicMeetingFee(bkOrder.getPublicMeetingFee());
        livingOrder.setPublicVipFee(bkOrder.getPublicVipFee());
        livingOrder.setPublicRoomFee(bkOrder.getPublicRoomFee());
        livingOrder.setTeamPeopleNumber(bkOrder.getTeamPeopleNumber());
        livingOrder.setBookingOrderId(bkOrder.getId());
        livingOrder.setThirdLoginUserId(bkOrder.getThirdLoginUserId());
        return livingOrder;
    }
    private BusRoomBookingOrders  copyBookingToAcceptOrder(BusRoomsLivingOrder  bkOrder) {
        BusRoomBookingOrders livingOrder = new BusRoomBookingOrders();
        livingOrder.setAgencyName(bkOrder.getAgencyName());
        livingOrder.setArrivalTime(bkOrder.getArrivalTime());
        livingOrder.setBreakfastNum(bkOrder.getBreakfastNum());
        livingOrder.setContactId(bkOrder.getContactId());
        livingOrder.setCreateTime(new Date());
        livingOrder.setCreateUser("");
        livingOrder.setCustomerSource(bkOrder.getCustomerSource());
        livingOrder.setBookingOrdersType(bkOrder.getLivingOrdersType());
        livingOrder.setBookingType(bkOrder.getLivingType());
        livingOrder.setOuterOrdersNo(bkOrder.getOuterOrdersNo());
        livingOrder.setContractTeamId(bkOrder.getContractTeamId());
        livingOrder.setContractTeamProtocolId(bkOrder.getContractTeamProtocolId());
        livingOrder.setDayCount(bkOrder.getDayCount());
        livingOrder.setCustomerType(bkOrder.getCustomerType());
        livingOrder.setDinnerNum(bkOrder.getDinnerNum());
        livingOrder.setLunchNum(bkOrder.getLunchNum());
        livingOrder.setDueOutTime(bkOrder.getDueOutTime());
        livingOrder.setHourRoomId(bkOrder.getHourRoomId());
        livingOrder.setWarranter(bkOrder.getWarranter());
        livingOrder.setWarrantType(bkOrder.getWarrantType());
        livingOrder.setVipCustomerId(bkOrder.getVipCustomerId());
        livingOrder.setTeamName(bkOrder.getTeamName());
        livingOrder.setTeamTypeDicId(bkOrder.getTeamTypeDicId());
        livingOrder.setRoomPriceSlnId(bkOrder.getRoomPriceSlnId());
        livingOrder.setLeaderName(bkOrder.getLeaderName());
        livingOrder.setPublicConsumeFee(bkOrder.getPublicConsumeFee());
        livingOrder.setPublicMeetingFee(bkOrder.getPublicMeetingFee());
        livingOrder.setPublicVipFee(bkOrder.getPublicVipFee());
        livingOrder.setPublicRoomFee(bkOrder.getPublicRoomFee());
        livingOrder.setTeamPeopleNumber(bkOrder.getTeamPeopleNumber());
        return livingOrder;
    }
    private BusRoomsLivingOrder copyBookingToAcceptOrder(BusBookingBatch bkOrder, BusRoomBookingOrders bookingOrder) {
        BusRoomsLivingOrder livingOrder = new BusRoomsLivingOrder();
        livingOrder.setAgencyName(bkOrder.getAgencyName());
        livingOrder.setArrivalTime(bkOrder.getArrivalTime());
        livingOrder.setBreakfastNum(bkOrder.getBreakfastNum());
        livingOrder.setContactId(bkOrder.getContactId());
        livingOrder.setCreateTime(new Date());
        livingOrder.setCreateUser("");
        livingOrder.setCustomerSource(bkOrder.getCustomerSource());
        livingOrder.setLivingOrdersType(BookingOrdersType.TEAM.getKey());
        livingOrder.setLivingType(bkOrder.getBookingType());
        livingOrder.setOuterOrdersNo(bkOrder.getOuterOrdersNo());
        livingOrder.setContractTeamId(bkOrder.getContractTeamId());
        livingOrder.setContractTeamProtocolId(bkOrder.getContractTeamProtocolId());
        livingOrder.setDayCount(bkOrder.getDayCount());
        livingOrder.setCustomerType(bkOrder.getCustomerType());
        livingOrder.setDinnerNum(bkOrder.getDinnerNum());
        livingOrder.setLunchNum(bkOrder.getLunchNum());
        livingOrder.setDueOutTime(bkOrder.getDueOutTime());
        livingOrder.setHourRoomId(bkOrder.getHourRoomId());
        livingOrder.setWarranter(bkOrder.getWarranter());
        livingOrder.setWarrantType(bkOrder.getWarrantType());
        livingOrder.setVipCustomerId(bkOrder.getVipCustomerId());
        livingOrder.setTeamName(bookingOrder.getTeamName());
        livingOrder.setTeamTypeDicId(bookingOrder.getTeamTypeDicId());
        livingOrder.setRoomPriceSlnId(bkOrder.getRoomPriceSlnId());
        livingOrder.setLeaderName(bookingOrder.getLeaderName());
        livingOrder.setPublicConsumeFee(bookingOrder.getPublicConsumeFee());
        livingOrder.setPublicMeetingFee(bookingOrder.getPublicMeetingFee());
        livingOrder.setPublicVipFee(bookingOrder.getPublicVipFee());
        livingOrder.setPublicRoomFee(bookingOrder.getPublicRoomFee());
        livingOrder.setTeamPeopleNumber(bookingOrder.getTeamPeopleNumber());
        livingOrder.setBookingOrderId(bkOrder.getId());
        return livingOrder;
    }
    private BusBookingBatch copyBatchToBase(BookingBatchRoomsDto data) {
        BusBookingBatch batch = new BookingBatchRoomsDto();
        batch.setBatchNo(data.getBatchNo());
        batch.setBookingOrdersId(data.getBookingOrdersId());
        batch.setArrivalTime(data.getArrivalTime());
        batch.setDueOutTime(data.getDueOutTime());
        batch.setDayCount(data.getDayCount());
        batch.setBreakfastNum(data.getBreakfastNum());
        batch.setLunchNum(data.getLunchNum());
        batch.setDinnerNum(data.getDinnerNum());
        batch.setCustomerType(data.getCustomerType());
        batch.setVipCustomerId(data.getVipCustomerId());
        batch.setContractTeamId(data.getContractTeamId());
        batch.setContractTeamProtocolId(data.getContractTeamProtocolId());
        batch.setRoomPriceSlnId(data.getRoomPriceSlnId());
        batch.setAgencyName(data.getAgencyName());
        batch.setContactId(data.getContactId());
        batch.setWarrantType(data.getWarrantType());
        batch.setWarranter(data.getWarranter());
        batch.setOuterOrdersNo(data.getOuterOrdersNo());
        batch.setRemark(data.getRemark());
        batch.setHourRoomId(data.getHourRoomId());
        batch.setCustomerSource(data.getCustomerSource());
        batch.setBookingType(data.getBookingType());
        batch.setBookingDicWay(data.getBookingDicWay());
        batch.setBookingStatus(data.getBookingStatus());
        return batch;
    }
    private BookingBatchRoomsVo copyBaseToBatch(BusBookingBatch data) {
        BookingBatchRoomsVo batch = new BookingBatchRoomsVo();
        batch.setBatchNo(data.getBatchNo());
        batch.setId(data.getId());
        batch.setBookingOrdersId(data.getBookingOrdersId());
        batch.setArrivalTime(data.getArrivalTime());
        batch.setDueOutTime(data.getDueOutTime());
        batch.setDayCount(data.getDayCount());
        batch.setBreakfastNum(data.getBreakfastNum());
        batch.setLunchNum(data.getLunchNum());
        batch.setDinnerNum(data.getDinnerNum());
        batch.setCustomerType(data.getCustomerType());
        batch.setVipCustomerId(data.getVipCustomerId());
        batch.setContractTeamId(data.getContractTeamId());
        batch.setContractTeamProtocolId(data.getContractTeamProtocolId());
        batch.setRoomPriceSlnId(data.getRoomPriceSlnId());
        batch.setAgencyName(data.getAgencyName());
        batch.setContactId(data.getContactId());
        batch.setWarrantType(data.getWarrantType());
        batch.setWarranter(data.getWarranter());
        batch.setOuterOrdersNo(data.getOuterOrdersNo());
        batch.setRemark(data.getRemark());
        batch.setHourRoomId(data.getHourRoomId());
        batch.setCustomerSource(data.getCustomerSource());
        batch.setBookingType(data.getBookingType());
        batch.setBookingDicWay(data.getBookingDicWay());
        batch.setBookingStatus(data.getBookingStatus());
        return batch;
    }
    void setBatchOrderInfo(BusRoomBookingOrders order, BookingBatchRoomsDto batchDto){
        batchDto.setBreakfastNum(order.getBreakfastNum());
        batchDto.setLunchNum(order.getLunchNum());
        batchDto.setDinnerNum(order.getDinnerNum());
        batchDto.setCustomerType(order.getCustomerType());
        batchDto.setVipCustomerId(order.getVipCustomerId());
        batchDto.setContractTeamId(order.getContractTeamId());
        batchDto.setContractTeamProtocolId(order.getContractTeamProtocolId());
        batchDto.setRoomPriceSlnId(order.getRoomPriceSlnId());
        batchDto.setAgencyName(order.getAgencyName());
        batchDto.setContactId(order.getContactId());
        batchDto.setWarrantType(order.getWarrantType());
        batchDto.setWarranter(order.getWarranter());
        batchDto.setOuterOrdersNo(order.getOuterOrdersNo());
        batchDto.setRemark(order.getRemark());
        batchDto.setHourRoomId(order.getHourRoomId());
        batchDto.setCustomerSource(order.getCustomerSource());
        batchDto.setBookingType(order.getBookingType());
        batchDto.setBookingDicWay(order.getBookingDicWay());
        batchDto.setBookingStatus(order.getBookingStatus());
    }
    void setBatchOrderInfoForEdit(BusRoomBookingOrders order, BookingBatchRoomsVo batchDto){
        batchDto.setBreakfastNum(order.getBreakfastNum());
        batchDto.setLunchNum(order.getLunchNum());
        batchDto.setDinnerNum(order.getDinnerNum());
        batchDto.setCustomerType(order.getCustomerType());
        batchDto.setVipCustomerId(order.getVipCustomerId());
        batchDto.setContractTeamId(order.getContractTeamId());
        batchDto.setContractTeamProtocolId(order.getContractTeamProtocolId());
        batchDto.setRoomPriceSlnId(order.getRoomPriceSlnId());
        batchDto.setAgencyName(order.getAgencyName());
        batchDto.setContactId(order.getContactId());
        batchDto.setWarrantType(order.getWarrantType());
        batchDto.setWarranter(order.getWarranter());
        batchDto.setOuterOrdersNo(order.getOuterOrdersNo());
        batchDto.setRemark(order.getRemark());
        batchDto.setHourRoomId(order.getHourRoomId());
        batchDto.setCustomerSource(order.getCustomerSource());
        batchDto.setBookingType(order.getBookingType());
        batchDto.setBookingDicWay(order.getBookingDicWay());
        batchDto.setBookingStatus(order.getBookingStatus());
    }

    /**
     * 根据手机号查询入住历史列表
     * @param hotelId
     * @param phone
     * @return
     */
    public Page<HashMap<String, Object>> ruZhuHistoryList(Page<HashMap<String, Object>> page,  String hotelId, String phone) {
        return page.setRecords(this.baseMapper.ruZhuHistoryList(page, hotelId, phone));
    }

    @Override
    public String delBusData(String hotelId) {
        List<CesRooms> hotelRooms = roomsService.list(Wrappers.<CesRooms>query().eq("hotel_id",hotelId));
        hotelRooms.forEach(s->{
            s.setRoomStatus(RoomStatusEnum.EMPTY_CLEAR.getKey());
        });
        roomsService.updateBatchById(hotelRooms);
        List<BusRoomBookingOrders> bookingOrders = list(Wrappers.<BusRoomBookingOrders>query().eq("hotel_id",hotelId));
        remove(Wrappers.<BusRoomBookingOrders>query().eq("hotel_id",hotelId));
        String delRes = "";
        if(bookingOrders.size() > 0) {
            List<String> bookingOrderIds = bookingOrders.stream().map(s->s.getId()).collect(Collectors.toList());
            bookingRoomsService.remove(Wrappers.<BusBookingRooms>query().in("booking_orders_id",bookingOrderIds));
            dayPriceService.remove(Wrappers.<BusBookingLayoutDayPrice>query().in("booking_order_id",bookingOrderIds));
            roomsLivingOrderService.remove(Wrappers.<BusRoomsLivingOrder>query().in("booking_order_id",bookingOrderIds));
            feeService.remove(Wrappers.<BusOrderFee>query().eq("hotel_id",hotelId));
            fwRoomCleanService.remove(Wrappers.<FwRoomClean>query().eq("hotel_id",hotelId));
            fwRoomLockService.remove(Wrappers.<FwRoomLock>query().eq("hotel_id",hotelId));
            fwRoomRepairService.remove(Wrappers.<FwRoomRepair>query().eq("hotel_id",hotelId));
        }
        return "";
    }

    // 换房
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean changeLivingRoom(LivingContinueVo param) {
        if(CollUtil.isEmpty(param.getLayoutDayPriceList())) throw new JeecgBootException("请传入房价");
        BusRoomsLivingOrder livingOrder = roomsLivingOrderService.getById(param.getId());
        if(livingOrder == null) throw  new JeecgBootException("入住记录不存在");
        BusBookingRooms bkRoom = bookingRoomsService.getById(livingOrder.getBookingRoomId());
        if(bkRoom == null) throw new JeecgBootException("入住房间记录不存在");
        CesRooms origLivingRoom = roomsService.getById(bkRoom.getRoomId());
        if(origLivingRoom == null) throw new JeecgBootException("原房间不存在");
        CesRooms changeRoom = roomsService.getById(param.getRoomId());
        if(changeRoom == null) throw new JeecgBootException("更换房间不存在");
        bkRoom.setRoomId(changeRoom.getId());
        bkRoom.setLayoutId(changeRoom.getLayoutId());
        bookingRoomsService.updateById(bkRoom);
        origLivingRoom.setLivingOrderId("");
        origLivingRoom.setRoomStatus(RoomStatusEnum.EMPTY_DIRTY.getKey());
        roomsService.updateById(origLivingRoom);
        changeRoom.setRoomStatus(RoomStatusEnum.LIVE_CLEAR.getKey());
        changeRoom.setLivingOrderId(param.getId());
        roomsService.updateById(changeRoom);
        feeService.update(Wrappers.<BusOrderFee>lambdaUpdate().set(BusOrderFee::getRoomId, changeRoom.getId())
                .eq(BusOrderFee::getLivingOrderId, param.getId()));
        livingLayoutDayPriceService.remove(Wrappers.<BusLivingLayoutDayPrice>query().eq("living_order_id",param.getId()));
        param.getLayoutDayPriceList().forEach(s->{
            s.setRoomLayoutId(changeRoom.getLayoutId());
            s.setRoomId(changeRoom.getId());
            s.setLivingType(1);
            s.setLivingOrderId(param.getId());
            if(DateUtils.formatDate(new Date()).equals(DateUtils.formatDate(s.getDayTime()))){
                // 修改当日房价 产生的当日房费账单也要改
                List<BusOrderFee> fees = feeService.list(Wrappers.<BusOrderFee>query()
                        .eq("DATE_FORMAT(day_time,'%Y-%m-%d')",DateUtils.formatDate(new Date())).eq("living_order_id",s.getLivingOrderId()));
                if(CollUtil.isNotEmpty(fees)) {
                    fees.get(0).setMoney(s.getPrice());
                    feeService.updateById(fees.get(0));
                }
            }
        });
        feeService.collectionBatch(param.getOrderFeeList(),livingOrder.getHotelId());
        livingLayoutDayPriceService.saveBatch(param.getLayoutDayPriceList());
        LambdaUpdateWrapper<BusRoomsLivingOrder> updateWrapper = new LambdaUpdateWrapper<>();
        livingOrder.setIsChangeRoom(true);
        roomsLivingOrderService.updateById(livingOrder);
        return true;
    }

    @Override
    public Boolean tuidan(List<String> feeIds) {
        if(feeIds == null || feeIds.size() == 0) throw new JeecgBootException("参数错误");
        feeIds.forEach(s->{
            tuidan(s);
        });
        return  true;

    }

    private  void  tuidan(String feeId){
        BusOrderFee fee = feeService.getById(feeId);
        if(fee==null) throw new JeecgBootException("费项不存在");
        if(fee.getSubjectType().equals(FeeSubjectType.MEI_RI_FANG_FEI.getKey()) || fee.getSubjectType().equals(FeeSubjectType.YA_JIN.getKey()) || fee.getSubjectType().equals(FeeSubjectType.YE_SHEN_FANG_FEI.getKey())){
            throw new JeecgBootException("房费和押金不能进行退单操作");
        }

        if(fee.getPreferentialStatus().equals(2)) {
            BusOrderFee settleFee = new BusOrderFee();
            settleFee.setPreferentialStatus(2);
            settleFee.setReturnFeeId(fee.getId());
            settleFee.setMoney(fee.getMoney().multiply(new BigDecimal(-1)));
            settleFee.setFeeType(2); // 收款
            settleFee.setCustorerOrderRemark("退单结账");
            settleFee.setAllowCouponscash(false);
            settleFee.setSubjectType(FeeSubjectType.TUI_DAN_JIE_ZHANG_SHOU_KUAN.getKey());
            settleFee.setHotelId(fee.getHotelId());
            settleFee.setCreateTime(new Date());
            settleFee.setPayTime(new Date());
            settleFee.setPreferentialStatus(2);
            settleFee.setLivingOrderId(fee.getLivingOrderId());
            settleFee.setRoomId(fee.getRoomId());
            feeService.save(settleFee);
        }
        fee.setReturnItem(true);
        fee.setPreferentialStatus(2);
        feeService.updateById(fee);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean reverseLiving(String livingOrderId, String hotelId) {
        BusCancel cancel = cancelService.getOne(Wrappers.<BusCancel>query().eq("hotel_id",hotelId));
        BusRoomsLivingOrder livingOrder = roomsLivingOrderService.getById(livingOrderId);
        if(cancel != null) {
            Calendar c1 = Calendar.getInstance();
            c1.setTime(livingOrder.getArrivalTime());
            Calendar c2 = Calendar.getInstance();
            c2.setTime(new Date());

            Integer diff = DateUtils.dateDiff('m',c1,c2);
            if(cancel.getStatus().equals(0)) throw new JeecgBootException("不允许撤销入住");
            if(Math.abs(diff)>cancel.getMinute()) throw new JeecgBootException("超过可撤销入住时间");
        }

        BusBookingRooms bkroom = bookingRoomsService.getById(livingOrder.getBookingRoomId());
        CesRooms room = roomsService.getById(bkroom.getRoomId());
        room.setRoomStatus(RoomStatusEnum.EMPTY_DIRTY.getKey());
        roomsService.updateById(room);
        feeService.remove(Wrappers.<BusOrderFee>query().eq("living_order_id", livingOrderId));
        roomsLivingOrderService.removeById(livingOrderId);
        return true;
    }

//    @Override
//    public Long count(String bookingOrderId,String hotelId) {
//        QueryWrapper<BusRoomsLivingOrder> qw = new QueryWrapper<>();
//        qw.eq("hotel_id", hotelId);
//        qw.eq("booking_order_id", bookingOrderId);
//        long count = roomsLivingOrderService.count(qw);
//        return count;
//    }

    @Override
    public Boolean canOnlyPaySelf(String bookingOrderId, String hotelId, String roomId) {
        QueryWrapper<BusRoomsLivingOrder> qw = new QueryWrapper<>();
        qw.eq("hotel_id", hotelId);
        qw.eq("booking_order_id", bookingOrderId);
        long count = roomsLivingOrderService.count(qw);
        BusBookingRooms bookingRooms = bookingRoomsService.getOne(Wrappers.<BusBookingRooms>query().eq("booking_orders_id", bookingOrderId).eq("room_id", roomId));
        if (count>1 && !bookingRooms.getIsMain()){
            return true;
        }
        return false;
    }

    @Override
    public Boolean payPortionFee(List<String> feeIds) {
        boolean isUpdate = feeService.update(Wrappers.<BusOrderFee>update().set("preferential_status", 2).in("id", feeIds));
        return isUpdate;
    }

    @Override
    public List<LivingContinueVo> getContinueOrder(String bookingOrderId, String hotelId) {
        List<LivingContinueVo> livingContinueList = baseMapper.getContinueOrder(bookingOrderId, hotelId);
        if (CollUtil.isEmpty(livingContinueList)){
            throw new JeecgBootException("订单未找到");
        }
        List<CesRoomLayout> layoutList = layoutService.list();
        livingContinueList.forEach(e -> {
            Optional<CesRoomLayout> layoutOptional = layoutList.stream().filter(layout -> layout.getId().equals(e.getLayoutId())).findFirst();
            layoutOptional.ifPresent(ele -> {
                    e.setRoomLayout(layoutOptional.get());
            });
        });
        return livingContinueList;

    }

    @Override
    public void addWillLeaveMess(String parameter) {
        BusHotel busHotel = busHotelService.getById(parameter);
        if (busHotel == null){
            log.info("通过id{}未找到酒店", parameter);
            return;
        }
        Set<Object> set = redisTemplate.opsForZSet().rangeByScore("sys:cache:message:willEndOrder", 0, Calendar.getInstance().getTimeInMillis());
        if (CollUtil.isEmpty(set)){
            return;
        }
        List<CesRooms> cesRooms = roomsService.list(Wrappers.<CesRooms>lambdaQuery().in(CesRooms::getLivingOrderId, set).eq(CesRooms::getHotelId, parameter));
        if (cesRooms.isEmpty()){
            return;
        }
        List<BusRoomsLivingOrder> roomsLivingOrders = roomsLivingOrderService.list(Wrappers.<BusRoomsLivingOrder>lambdaQuery()
                .in(BusRoomsLivingOrder::getId, set).eq(BusRoomsLivingOrder::getHotelId, parameter));
        LambdaQueryWrapper<SysUser> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(SysUser::getDelFlag, 0);
        lambdaQueryWrapper.eq(SysUser::getRelTenantIds, busHotel.getTenantId());
        List<SysUser> sysUsers = sysUserService.list(lambdaQueryWrapper);
        String userIds = "";
        for (SysUser sysUser : sysUsers) {
            userIds += sysUser.getUsername() + ",";
        }
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
        String finalUserIds = userIds;
        set.forEach(e -> {
            Optional<BusRoomsLivingOrder> orderOptional = roomsLivingOrders.stream().filter(ele -> ele.getId().equals(e)).findFirst();
            Optional<CesRooms> roomsOptional = cesRooms.stream().filter(ele -> ele.getLivingOrderId().equals(e)).findFirst();
            if (orderOptional.isPresent() && roomsOptional.isPresent()){
                    /*添加消息通知*/
                String mesStr = roomsOptional.get().getPrefix() == null ? "" : roomsOptional.get().getPrefix()
                        + roomsOptional.get().getName() + "房间 " + LivingTypeEnum.val(orderOptional.get().getLivingType()).getTitle() + "即将到达预离时间";
                MessageDTO messageDTO = new MessageDTO();
                messageDTO.setFromUser("");
                messageDTO.setToUser(finalUserIds);
                messageDTO.setTenantId(busHotel.getTenantId());
                messageDTO.setTitle(mesStr);
                messageDTO.setContent(mesStr);
                messageDTO.setCategory(CommonConstant.MSG_CATEGORY_1);
                messageDTO.setMsgCategoryDetail(CommonConstant.MSG_ROOM);
                sysBaseAPI.sendSysAnnouncement(messageDTO);
                redisTemplate.opsForZSet().remove("sys:cache:message:willEndOrder", e);
            }
        });
    }

    @Override
    public void overTimerBooking(String hotelId) {
        Set<Object> set = redisTemplate.opsForZSet().rangeByScore("sys:cache:order:overTimerBooking", 0, Calendar.getInstance().getTimeInMillis());
        if (CollUtil.isEmpty(set)){
            return;
        }
        List<BusRoomBookingOrders> bookingOrders = list(Wrappers.<BusRoomBookingOrders>lambdaQuery().in(BusRoomBookingOrders::getId, set)
                .eq(BusRoomBookingOrders::getBookingStatus, BookingStatusTypeEnum.NORMAL.getKey()).eq(BusRoomBookingOrders::getHotelId, hotelId));
        List<String> bookingIds = bookingOrders.stream().map(BusRoomBookingOrders::getId).collect(Collectors.toList());
        if (bookingIds.isEmpty()){
            return;
        }
        List<BusBookingRooms> bookingRoomsList = bookingRoomsService.list(Wrappers.<BusBookingRooms>lambdaQuery().in(BusBookingRooms::getBookingOrdersId, bookingIds));
        Set<String> roomIds = bookingRoomsList.stream().map(BusBookingRooms::getRoomId).collect(Collectors.toSet());
        if (!roomIds.isEmpty()){
            List<CesRooms> cesRoomList = roomsService.list(Wrappers.<CesRooms>lambdaQuery().in(CesRooms::getId, roomIds).eq(CesRooms::getRoomStatus, RoomStatusEnum.RESERVE.getKey()));
            cesRoomList.forEach(e -> e.setRoomStatus(e.getLastRoomStatus()));
            roomsService.updateBatchById(cesRoomList);
        }
        bookingOrders.forEach(e -> {
            e.setBookingStatus(BookingStatusTypeEnum.INVALID.getKey());
            redisTemplate.opsForZSet().remove("sys:cache:order:overTimerBooking", e.getId());
        });
        updateBatchById(bookingOrders);
    }


}
